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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/bin/aztomiq.js +336 -0
  4. package/bin/create-aztomiq.js +77 -0
  5. package/package.json +58 -0
  6. package/scripts/analyze-screenshots.js +217 -0
  7. package/scripts/build.js +39 -0
  8. package/scripts/builds/admin.js +17 -0
  9. package/scripts/builds/assets.js +167 -0
  10. package/scripts/builds/cache.js +48 -0
  11. package/scripts/builds/config.js +31 -0
  12. package/scripts/builds/data.js +210 -0
  13. package/scripts/builds/pages.js +288 -0
  14. package/scripts/builds/playground-examples.js +50 -0
  15. package/scripts/builds/templates.js +118 -0
  16. package/scripts/builds/utils.js +37 -0
  17. package/scripts/create-bug-tracker.js +277 -0
  18. package/scripts/deploy.js +135 -0
  19. package/scripts/feedback-generator.js +102 -0
  20. package/scripts/ui-test.js +624 -0
  21. package/scripts/utils/extract-examples.js +44 -0
  22. package/scripts/utils/migrate-icons.js +67 -0
  23. package/src/includes/breadcrumbs.ejs +100 -0
  24. package/src/includes/cloud-tags.ejs +120 -0
  25. package/src/includes/footer.ejs +37 -0
  26. package/src/includes/generator.ejs +226 -0
  27. package/src/includes/head.ejs +73 -0
  28. package/src/includes/header-data-only.ejs +43 -0
  29. package/src/includes/header.ejs +71 -0
  30. package/src/includes/layout.ejs +68 -0
  31. package/src/includes/legacy-banner.ejs +19 -0
  32. package/src/includes/mega-menu.ejs +80 -0
  33. package/src/includes/schema.ejs +20 -0
  34. package/src/templates/manifest.json.ejs +30 -0
  35. package/src/templates/readme-dist.md.ejs +58 -0
  36. package/src/templates/robots.txt.ejs +4 -0
  37. package/src/templates/sitemap.xml.ejs +69 -0
  38. 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();