aztomiq 1.0.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/LICENSE +21 -0
- package/README.md +146 -0
- package/bin/aztomiq.js +336 -0
- package/bin/create-aztomiq.js +77 -0
- package/package.json +58 -0
- package/scripts/analyze-screenshots.js +217 -0
- package/scripts/build.js +39 -0
- package/scripts/builds/admin.js +17 -0
- package/scripts/builds/assets.js +167 -0
- package/scripts/builds/cache.js +48 -0
- package/scripts/builds/config.js +31 -0
- package/scripts/builds/data.js +210 -0
- package/scripts/builds/pages.js +288 -0
- package/scripts/builds/playground-examples.js +50 -0
- package/scripts/builds/templates.js +118 -0
- package/scripts/builds/utils.js +37 -0
- package/scripts/create-bug-tracker.js +277 -0
- package/scripts/deploy.js +135 -0
- package/scripts/feedback-generator.js +102 -0
- package/scripts/ui-test.js +624 -0
- package/scripts/utils/extract-examples.js +44 -0
- package/scripts/utils/migrate-icons.js +67 -0
- package/src/includes/breadcrumbs.ejs +100 -0
- package/src/includes/cloud-tags.ejs +120 -0
- package/src/includes/footer.ejs +37 -0
- package/src/includes/generator.ejs +226 -0
- package/src/includes/head.ejs +73 -0
- package/src/includes/header-data-only.ejs +43 -0
- package/src/includes/header.ejs +71 -0
- package/src/includes/layout.ejs +68 -0
- package/src/includes/legacy-banner.ejs +19 -0
- package/src/includes/mega-menu.ejs +80 -0
- package/src/includes/schema.ejs +20 -0
- package/src/templates/manifest.json.ejs +30 -0
- package/src/templates/readme-dist.md.ejs +58 -0
- package/src/templates/robots.txt.ejs +4 -0
- package/src/templates/sitemap.xml.ejs +69 -0
- package/src/templates/sw.js.ejs +78 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UI Bug Tracker
|
|
5
|
+
* Analyzes screenshots and creates structured bug report
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs').promises;
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const SCREENSHOTS_DIR = './ui-test-results/screenshots';
|
|
12
|
+
const OUTPUT_FILE = './plans/0-WAR-ROOM/UI-BUGS-TRACKER.md';
|
|
13
|
+
|
|
14
|
+
async function createBugTracker() {
|
|
15
|
+
console.log('🐛 Creating UI Bug Tracker...\n');
|
|
16
|
+
|
|
17
|
+
const files = await fs.readdir(SCREENSHOTS_DIR);
|
|
18
|
+
const desktopScreenshots = files.filter(f => f.includes('_desktop.png'));
|
|
19
|
+
|
|
20
|
+
// Group by tool
|
|
21
|
+
const tools = {};
|
|
22
|
+
desktopScreenshots.forEach(file => {
|
|
23
|
+
const match = file.match(/_(vi|en)_(.+?)_desktop\.png/);
|
|
24
|
+
if (match) {
|
|
25
|
+
const [, lang, toolName] = match;
|
|
26
|
+
if (!tools[toolName]) {
|
|
27
|
+
tools[toolName] = { vi: false, en: false };
|
|
28
|
+
}
|
|
29
|
+
tools[toolName][lang] = true;
|
|
30
|
+
} else {
|
|
31
|
+
// Homepage
|
|
32
|
+
const homeLang = file.includes('_vi_') ? 'vi' : 'en';
|
|
33
|
+
if (!tools['homepage']) {
|
|
34
|
+
tools['homepage'] = { vi: false, en: false };
|
|
35
|
+
}
|
|
36
|
+
tools['homepage'][homeLang] = true;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const report = `# 🐛 UI Bugs Tracker
|
|
41
|
+
|
|
42
|
+
**Created**: ${new Date().toLocaleString()}
|
|
43
|
+
**Total Tools**: ${Object.keys(tools).length}
|
|
44
|
+
**Status**: In Progress
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 📋 HOW TO USE THIS TRACKER
|
|
49
|
+
|
|
50
|
+
### Step 1: Review Screenshots
|
|
51
|
+
\`\`\`bash
|
|
52
|
+
open ui-test-results/screenshots/
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
### Step 2: For Each Tool, Check:
|
|
56
|
+
- [ ] **Layout**: Form width, alignment, spacing
|
|
57
|
+
- [ ] **Labels**: Visible, clear, properly sized
|
|
58
|
+
- [ ] **Inputs**: Border, focus state, sizing
|
|
59
|
+
- [ ] **Buttons**: Prominent, well-placed
|
|
60
|
+
- [ ] **Results**: Clear display, good formatting
|
|
61
|
+
- [ ] **Mobile**: Responsive behavior (check mobile screenshots)
|
|
62
|
+
- [ ] **Borders**: All containers have visible borders
|
|
63
|
+
- [ ] **Spacing**: Consistent gaps (1.5-2rem)
|
|
64
|
+
|
|
65
|
+
### Step 3: Log Issues Below
|
|
66
|
+
For each bug found, add to the appropriate section.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 🔴 CRITICAL BUGS (Breaks Functionality)
|
|
71
|
+
|
|
72
|
+
### Template
|
|
73
|
+
\`\`\`markdown
|
|
74
|
+
#### [Tool Name] - [Issue Title]
|
|
75
|
+
**Severity**: Critical
|
|
76
|
+
**Pages**: VI, EN (or specify)
|
|
77
|
+
**Description**: What's broken
|
|
78
|
+
**Impact**: How it affects users
|
|
79
|
+
**Screenshot**: \`tool-name_desktop.png\`
|
|
80
|
+
**Fix Required**: What needs to be done
|
|
81
|
+
**ETA**: Estimated time to fix
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
### Issues
|
|
85
|
+
<!-- Add critical bugs here -->
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🟡 HIGH PRIORITY (Major UX Issues)
|
|
90
|
+
|
|
91
|
+
### Template
|
|
92
|
+
\`\`\`markdown
|
|
93
|
+
#### [Tool Name] - [Issue Title]
|
|
94
|
+
**Severity**: High
|
|
95
|
+
**Pages**: VI, EN
|
|
96
|
+
**Description**: What's wrong
|
|
97
|
+
**Impact**: UX degradation
|
|
98
|
+
**Screenshot**: \`tool-name_desktop.png\`
|
|
99
|
+
**Fix Required**: What to change
|
|
100
|
+
**ETA**: Time estimate
|
|
101
|
+
\`\`\`
|
|
102
|
+
|
|
103
|
+
### Issues
|
|
104
|
+
<!-- Add high priority bugs here -->
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 🟢 MEDIUM PRIORITY (Visual/Polish Issues)
|
|
109
|
+
|
|
110
|
+
### Template
|
|
111
|
+
\`\`\`markdown
|
|
112
|
+
#### [Tool Name] - [Issue Title]
|
|
113
|
+
**Severity**: Medium
|
|
114
|
+
**Pages**: VI, EN
|
|
115
|
+
**Description**: Visual issue
|
|
116
|
+
**Impact**: Cosmetic
|
|
117
|
+
**Screenshot**: \`tool-name_desktop.png\`
|
|
118
|
+
**Fix Required**: Polish needed
|
|
119
|
+
**ETA**: Time estimate
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
### Issues
|
|
123
|
+
<!-- Add medium priority bugs here -->
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 🔵 LOW PRIORITY (Minor Issues)
|
|
128
|
+
|
|
129
|
+
### Issues
|
|
130
|
+
<!-- Add low priority bugs here -->
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## ✅ TOOLS REVIEWED
|
|
135
|
+
|
|
136
|
+
Track which tools have been reviewed for bugs:
|
|
137
|
+
|
|
138
|
+
${Object.keys(tools).sort().map(tool => {
|
|
139
|
+
const hasVi = tools[tool].vi;
|
|
140
|
+
const hasEn = tools[tool].en;
|
|
141
|
+
return `- [ ] **${tool}** ${hasVi ? '(VI ✓)' : ''} ${hasEn ? '(EN ✓)' : ''}`;
|
|
142
|
+
}).join('\n')}
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 📊 BUG STATISTICS
|
|
147
|
+
|
|
148
|
+
| Priority | Count | % |
|
|
149
|
+
|----------|-------|---|
|
|
150
|
+
| Critical | 0 | 0% |
|
|
151
|
+
| High | 0 | 0% |
|
|
152
|
+
| Medium | 0 | 0% |
|
|
153
|
+
| Low | 0 | 0% |
|
|
154
|
+
| **Total** | **0** | **100%** |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 🎯 COMMON PATTERNS
|
|
159
|
+
|
|
160
|
+
### Form Issues
|
|
161
|
+
- [ ] Input width too wide
|
|
162
|
+
- [ ] Labels too small/muted
|
|
163
|
+
- [ ] No max-width constraint
|
|
164
|
+
- [ ] Poor spacing
|
|
165
|
+
|
|
166
|
+
### Layout Issues
|
|
167
|
+
- [ ] Content not centered
|
|
168
|
+
- [ ] Missing borders
|
|
169
|
+
- [ ] Inconsistent spacing
|
|
170
|
+
- [ ] Alignment problems
|
|
171
|
+
|
|
172
|
+
### Mobile Issues
|
|
173
|
+
- [ ] Horizontal scroll
|
|
174
|
+
- [ ] Text too small
|
|
175
|
+
- [ ] Buttons too small
|
|
176
|
+
- [ ] Poor touch targets
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🔧 QUICK FIXES
|
|
181
|
+
|
|
182
|
+
### Global CSS Issues
|
|
183
|
+
If multiple tools have same issue, fix in \`global.css\`:
|
|
184
|
+
\`\`\`css
|
|
185
|
+
/* Example: All forms need max-width */
|
|
186
|
+
.input-section {
|
|
187
|
+
max-width: 700px;
|
|
188
|
+
margin: 0 auto;
|
|
189
|
+
}
|
|
190
|
+
\`\`\`
|
|
191
|
+
|
|
192
|
+
### Per-Tool CSS Issues
|
|
193
|
+
If issue is specific to one tool, fix in \`src/features/[tool]/style.css\`
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 📝 REVIEW PROCESS
|
|
198
|
+
|
|
199
|
+
1. **Open screenshots folder**
|
|
200
|
+
2. **Review each tool** (desktop + mobile)
|
|
201
|
+
3. **Compare with checklist**
|
|
202
|
+
4. **Log issues** in appropriate section
|
|
203
|
+
5. **Mark tool as reviewed**
|
|
204
|
+
6. **Update statistics**
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 🚀 FIX WORKFLOW
|
|
209
|
+
|
|
210
|
+
1. **Prioritize**: Start with Critical, then High
|
|
211
|
+
2. **Group**: Fix similar issues together
|
|
212
|
+
3. **Test**: Run UI test after each batch
|
|
213
|
+
4. **Verify**: Check screenshots
|
|
214
|
+
5. **Mark done**: Move to completed section
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## ✅ COMPLETED FIXES
|
|
219
|
+
|
|
220
|
+
### Template
|
|
221
|
+
\`\`\`markdown
|
|
222
|
+
#### [Tool Name] - [Issue Title]
|
|
223
|
+
**Fixed**: YYYY-MM-DD
|
|
224
|
+
**Solution**: What was changed
|
|
225
|
+
**Verified**: Screenshot comparison
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
### Fixes
|
|
229
|
+
<!-- Add completed fixes here -->
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 📸 SCREENSHOT REFERENCE
|
|
234
|
+
|
|
235
|
+
All screenshots available in:
|
|
236
|
+
\`\`\`
|
|
237
|
+
ui-test-results/screenshots/
|
|
238
|
+
├── _vi__desktop.png (Homepage VI)
|
|
239
|
+
├── _en__desktop.png (Homepage EN)
|
|
240
|
+
├── _vi_[tool]_desktop.png
|
|
241
|
+
├── _vi_[tool]_mobile.png
|
|
242
|
+
└── ... (${desktopScreenshots.length * 2} total)
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 💡 TIPS
|
|
248
|
+
|
|
249
|
+
1. **Use screenshot names** to identify tools
|
|
250
|
+
2. **Check both VI and EN** versions
|
|
251
|
+
3. **Compare desktop vs mobile**
|
|
252
|
+
4. **Look for patterns** (same issue across tools)
|
|
253
|
+
5. **Fix globally** when possible
|
|
254
|
+
6. **Test incrementally** (don't fix everything at once)
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
**Last Updated**: ${new Date().toLocaleString()}
|
|
259
|
+
**Status**: Ready for review
|
|
260
|
+
**Next**: Start reviewing screenshots and logging bugs
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
await fs.writeFile(OUTPUT_FILE, report);
|
|
264
|
+
console.log(`✅ Bug tracker created: ${OUTPUT_FILE}\n`);
|
|
265
|
+
console.log('📊 SUMMARY');
|
|
266
|
+
console.log('─'.repeat(50));
|
|
267
|
+
console.log(`Total Tools: ${Object.keys(tools).length}`);
|
|
268
|
+
console.log(`Screenshots: ${desktopScreenshots.length * 2} (desktop + mobile)`);
|
|
269
|
+
console.log('─'.repeat(50));
|
|
270
|
+
console.log(`\n💡 Next Steps:`);
|
|
271
|
+
console.log(`1. Open screenshots: open ${SCREENSHOTS_DIR}`);
|
|
272
|
+
console.log(`2. Open tracker: open ${OUTPUT_FILE}`);
|
|
273
|
+
console.log(`3. Review and log bugs`);
|
|
274
|
+
console.log(`4. Fix by priority\n`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
createBugTracker().catch(console.error);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const yaml = require('js-yaml');
|
|
5
|
+
|
|
6
|
+
// Load Config
|
|
7
|
+
const ROOT_DIR = path.join(__dirname, '..');
|
|
8
|
+
const globalConfigPath = path.join(ROOT_DIR, 'src/data/global.yaml');
|
|
9
|
+
// 1. Initial Defaults
|
|
10
|
+
const defaults = {
|
|
11
|
+
branch: 'gh-pages',
|
|
12
|
+
remote: 'origin',
|
|
13
|
+
dist_folder: 'dist',
|
|
14
|
+
strategy: 'init'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// 2. Parse CLI Arguments (e.g. --branch=prod)
|
|
18
|
+
const args = process.argv.slice(2).reduce((acc, arg) => {
|
|
19
|
+
if (arg.startsWith('--')) {
|
|
20
|
+
const [key, value] = arg.split('=');
|
|
21
|
+
acc[key.replace('--', '').replace('-', '_')] = value || true;
|
|
22
|
+
}
|
|
23
|
+
return acc;
|
|
24
|
+
}, {});
|
|
25
|
+
|
|
26
|
+
// 3. Load Global Config
|
|
27
|
+
let yamlConfig = {};
|
|
28
|
+
try {
|
|
29
|
+
if (fs.existsSync(globalConfigPath)) {
|
|
30
|
+
const parsed = yaml.load(fs.readFileSync(globalConfigPath, 'utf8'));
|
|
31
|
+
// Merge order: defaults < yaml.build.deploy < yaml.deployment
|
|
32
|
+
const buildDeploy = parsed.build?.deploy || {};
|
|
33
|
+
const topDeployment = parsed.deployment || {};
|
|
34
|
+
|
|
35
|
+
// Normalize keys (handle "folder" as "dist_folder")
|
|
36
|
+
const normalize = (obj) => {
|
|
37
|
+
if (obj.folder) {
|
|
38
|
+
obj.dist_folder = obj.folder;
|
|
39
|
+
delete obj.folder;
|
|
40
|
+
}
|
|
41
|
+
return obj;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
yamlConfig = { ...normalize(buildDeploy), ...normalize(topDeployment) };
|
|
45
|
+
}
|
|
46
|
+
} catch (e) {
|
|
47
|
+
console.warn('⚠️ Warning: Failed to parse global.yaml, using defaults.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 4. Final Config (Priority: CLI > YAML > Defaults)
|
|
51
|
+
const config = { ...defaults, ...yamlConfig, ...args };
|
|
52
|
+
const { branch, remote, dist_folder, strategy } = config;
|
|
53
|
+
|
|
54
|
+
console.time('🚀 Deployment Duration');
|
|
55
|
+
console.log('🚀 Starting Deployment...');
|
|
56
|
+
console.log(`📡 Target: ${remote}/${branch}`);
|
|
57
|
+
console.log(`📂 Folder: ${dist_folder}`);
|
|
58
|
+
console.log(`🛠️ Strategy: ${strategy}`);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// 1. Build
|
|
62
|
+
console.log('📦 Building Production...');
|
|
63
|
+
execSync('npm run build', { stdio: 'inherit' });
|
|
64
|
+
|
|
65
|
+
// 2. Ensure dist exists
|
|
66
|
+
const deployDir = path.resolve(ROOT_DIR, dist_folder);
|
|
67
|
+
if (!fs.existsSync(deployDir)) {
|
|
68
|
+
throw new Error(`Build folder "${dist_folder}" not found.`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 4. Deployment
|
|
72
|
+
if (strategy === 'init') {
|
|
73
|
+
// Strategy: Init new repo in dist and force push (Good for external repo deploy)
|
|
74
|
+
console.log('☁️ Deploying via git init strategy...');
|
|
75
|
+
|
|
76
|
+
// Commands run inside dist folder
|
|
77
|
+
execSync('git init', { cwd: deployDir, stdio: 'inherit' });
|
|
78
|
+
|
|
79
|
+
// Set default branch for new repo to main/master to avoid issues
|
|
80
|
+
try { execSync('git checkout -b main', { cwd: deployDir, stdio: 'quiet' }); } catch (e) { }
|
|
81
|
+
|
|
82
|
+
// Ensure the current directory is safe
|
|
83
|
+
try { execSync('git config --global --add safe.directory ' + deployDir, { stdio: 'inherit' }); } catch (e) { }
|
|
84
|
+
|
|
85
|
+
execSync('git add .', { cwd: deployDir, stdio: 'inherit' });
|
|
86
|
+
try {
|
|
87
|
+
execSync('git commit -m "🚀 Deploy: ' + new Date().toISOString() + '"', { cwd: deployDir, stdio: 'inherit' });
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.log('⚠️ Nothing to commit, proceeding to push.');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Determine the actual URL
|
|
93
|
+
let remoteUrl = remote;
|
|
94
|
+
const isUrl = remote.includes('://') || remote.startsWith('git@');
|
|
95
|
+
|
|
96
|
+
if (!isUrl) {
|
|
97
|
+
try {
|
|
98
|
+
remoteUrl = execSync(`git remote get-url ${remote}`).toString().trim();
|
|
99
|
+
} catch (e) {
|
|
100
|
+
console.error(`❌ Error: Could not resolve remote name "${remote}" to a URL.`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Add remote to the dist repo
|
|
106
|
+
try {
|
|
107
|
+
execSync(`git remote add deploy-remote "${remoteUrl}"`, { cwd: deployDir, stdio: 'inherit' });
|
|
108
|
+
} catch (e) {
|
|
109
|
+
execSync(`git remote set-url deploy-remote "${remoteUrl}"`, { cwd: deployDir, stdio: 'inherit' });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const pushCmd = `git push --force deploy-remote HEAD:${branch}`;
|
|
113
|
+
console.log(`> ${pushCmd}`);
|
|
114
|
+
execSync(pushCmd, { cwd: deployDir, stdio: 'inherit' });
|
|
115
|
+
|
|
116
|
+
} else {
|
|
117
|
+
// Strategy: Git Subtree (Good for same-repo gh-pages)
|
|
118
|
+
console.log('☁️ Pushing subtree...');
|
|
119
|
+
|
|
120
|
+
// Ensure dist is committed first (subtree needs it)
|
|
121
|
+
try { execSync(`git add ${dist_folder} -f`, { stdio: 'inherit' }); } catch (e) { }
|
|
122
|
+
try { execSync('git commit -m "🚀 Deploy: Update dist"', { stdio: 'inherit' }); } catch (e) { }
|
|
123
|
+
|
|
124
|
+
const cmd = `git subtree push --prefix ${dist_folder} ${remote} ${branch}`;
|
|
125
|
+
console.log(`> ${cmd}`);
|
|
126
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('✅ Deployed Successfully!');
|
|
130
|
+
console.timeEnd('🚀 Deployment Duration');
|
|
131
|
+
|
|
132
|
+
} catch (e) {
|
|
133
|
+
console.error('❌ Deployment Failed:', e.message);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const RESULTS_FILE = './ui-test-results/results.json';
|
|
5
|
+
const OUTPUT_FILE = './feedback.md';
|
|
6
|
+
|
|
7
|
+
async function generateFeedback() {
|
|
8
|
+
console.log('🤖 Generating Realistic Feedback...');
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const resultsData = await fs.readFile(RESULTS_FILE, 'utf-8');
|
|
12
|
+
const results = JSON.parse(resultsData);
|
|
13
|
+
|
|
14
|
+
let feedbackContent = `# Comprehensive Feature Feedback\n\n`;
|
|
15
|
+
feedbackContent += `**Date**: ${new Date().toLocaleString()}\n`;
|
|
16
|
+
feedbackContent += `**Total Pages Reviewed**: ${results.summary.total}\n\n`;
|
|
17
|
+
feedbackContent += `This document contains realistic feedback based on automated UI testing and simulated user experience review.\n\n`;
|
|
18
|
+
feedbackContent += `---\n\n`;
|
|
19
|
+
|
|
20
|
+
// Group by feature/tool name
|
|
21
|
+
// URLs are like http://localhost:3000/en/bmi-calculator/
|
|
22
|
+
// We want to group everything related to "bmi-calculator"
|
|
23
|
+
|
|
24
|
+
const features = {};
|
|
25
|
+
|
|
26
|
+
results.pages.forEach(page => {
|
|
27
|
+
// Extract feature name from URL.
|
|
28
|
+
// e.g., http://localhost:3000/vi/tool-name/
|
|
29
|
+
const match = page.url.match(/localhost:3000\/(?:vi|en)\/([^\/]+)/);
|
|
30
|
+
let featureName = 'Homepage';
|
|
31
|
+
if (match && match[1]) {
|
|
32
|
+
featureName = match[1];
|
|
33
|
+
} else if (page.url.includes('/vi/') || page.url.includes('/en/')) {
|
|
34
|
+
if (page.url.endsWith('/vi/') || page.url.endsWith('/en/')) featureName = 'Homepage';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!features[featureName]) {
|
|
38
|
+
features[featureName] = {
|
|
39
|
+
pages: []
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
features[featureName].pages.push(page);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
for (const [featureName, data] of Object.entries(features)) {
|
|
46
|
+
feedbackContent += `## 🛠 ${featureName.toUpperCase().replace(/-/g, ' ')}\n\n`;
|
|
47
|
+
|
|
48
|
+
let featureStatus = '✅ Excellent';
|
|
49
|
+
let feedbackPoints = [];
|
|
50
|
+
|
|
51
|
+
// Analyze pages for this feature
|
|
52
|
+
data.pages.forEach(page => {
|
|
53
|
+
const lang = page.url.includes('/vi/') ? 'Vietnamese' : 'English';
|
|
54
|
+
|
|
55
|
+
if (page.status === 'failed') {
|
|
56
|
+
featureStatus = '🔴 Critical Issues';
|
|
57
|
+
feedbackPoints.push(`- **${lang} Version**: fatal errors detected.`);
|
|
58
|
+
} else if (page.status === 'warning' && featureStatus !== '🔴 Critical Issues') {
|
|
59
|
+
featureStatus = '⚠️ Needs Polish';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Specific Feedback Generation
|
|
63
|
+
if (page.issues.length > 0) {
|
|
64
|
+
page.issues.forEach(issue => {
|
|
65
|
+
if (issue.type === 'i18n') {
|
|
66
|
+
feedbackPoints.push(`- **${lang}**: Found ${issue.count} untranslated text keys (e.g., "${issue.details[0]?.key}"). It makes the app look unfinished.`);
|
|
67
|
+
}
|
|
68
|
+
if (issue.type === 'css') {
|
|
69
|
+
feedbackPoints.push(`- **${lang}**: The layout has issues. ${issue.details[0]?.message || 'Elements are overflowing or misaligned'}.`);
|
|
70
|
+
}
|
|
71
|
+
if (issue.type === 'console-errors') {
|
|
72
|
+
feedbackPoints.push(`- **${lang}**: There are JavaScript errors in the console. Functionality might be broken. Error: "${issue.details[0]?.substring(0, 100)}..."`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (feedbackPoints.length === 0) {
|
|
79
|
+
feedbackPoints.push(`- Both language versions look clean and load without visible errors.`);
|
|
80
|
+
feedbackPoints.push(`- Layout and responsiveness appear stable.`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
feedbackContent += `**Overall Status**: ${featureStatus}\n\n`;
|
|
84
|
+
feedbackContent += `### 📝 User Feedback\n`;
|
|
85
|
+
// Deduplicate feedback points
|
|
86
|
+
const uniquePoints = [...new Set(feedbackPoints)];
|
|
87
|
+
uniquePoints.forEach(point => {
|
|
88
|
+
feedbackContent += `${point}\n`;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
feedbackContent += `\n---\n\n`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await fs.writeFile(OUTPUT_FILE, feedbackContent);
|
|
95
|
+
console.log(`✅ Feedback generated at: ${OUTPUT_FILE}`);
|
|
96
|
+
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error('❌ Error generating feedback:', err);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
generateFeedback();
|