@oalacea/daemon 0.6.4 → 0.7.1
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/README.md +268 -58
- package/bin/Dockerfile +158 -16
- package/bin/docker-entrypoint.sh +293 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +22 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands/command.types.d.ts +216 -0
- package/dist/cli/commands/command.types.d.ts.map +1 -0
- package/dist/cli/commands/command.types.js +64 -0
- package/dist/cli/commands/command.types.js.map +1 -0
- package/dist/cli/commands/history.command.d.ts +91 -0
- package/dist/cli/commands/history.command.d.ts.map +1 -0
- package/dist/cli/commands/history.command.js +336 -0
- package/dist/cli/commands/history.command.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -3
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +7 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/optimize.command.d.ts +110 -0
- package/dist/cli/commands/optimize.command.d.ts.map +1 -0
- package/dist/cli/commands/optimize.command.js +497 -0
- package/dist/cli/commands/optimize.command.js.map +1 -0
- package/dist/cli/commands/report.command.d.ts +110 -0
- package/dist/cli/commands/report.command.d.ts.map +1 -0
- package/dist/cli/commands/report.command.js +532 -0
- package/dist/cli/commands/report.command.js.map +1 -0
- package/dist/cli/commands/review.command.d.ts +110 -0
- package/dist/cli/commands/review.command.d.ts.map +1 -0
- package/dist/cli/commands/review.command.js +520 -0
- package/dist/cli/commands/review.command.js.map +1 -0
- package/dist/cli/commands/score.command.d.ts +47 -0
- package/dist/cli/commands/score.command.d.ts.map +1 -0
- package/dist/cli/commands/score.command.js +261 -0
- package/dist/cli/commands/score.command.js.map +1 -0
- package/dist/cli/utils/index.d.ts +10 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/index.js +10 -0
- package/dist/cli/utils/index.js.map +1 -0
- package/dist/cli/utils/output.d.ts +192 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +411 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/cli/utils/progress.d.ts +204 -0
- package/dist/cli/utils/progress.d.ts.map +1 -0
- package/dist/cli/utils/progress.js +396 -0
- package/dist/cli/utils/progress.js.map +1 -0
- package/dist/core/types/index.d.ts +1 -0
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/project.types.d.ts +3 -3
- package/dist/core/types/project.types.d.ts.map +1 -1
- package/dist/core/types/scoring.types.d.ts +301 -0
- package/dist/core/types/scoring.types.d.ts.map +1 -0
- package/dist/core/types/scoring.types.js +8 -0
- package/dist/core/types/scoring.types.js.map +1 -0
- package/dist/services/detection/framework-detector.d.ts.map +1 -1
- package/dist/services/detection/framework-detector.js +74 -5
- package/dist/services/detection/framework-detector.js.map +1 -1
- package/dist/services/index.d.ts +12 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +14 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/optimization/detectors/bug-detector.d.ts +82 -0
- package/dist/services/optimization/detectors/bug-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/bug-detector.js +443 -0
- package/dist/services/optimization/detectors/bug-detector.js.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts +108 -0
- package/dist/services/optimization/detectors/code-smell-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/code-smell-detector.js +569 -0
- package/dist/services/optimization/detectors/code-smell-detector.js.map +1 -0
- package/dist/services/optimization/detectors/index.d.ts +7 -0
- package/dist/services/optimization/detectors/index.d.ts.map +1 -0
- package/dist/services/optimization/detectors/index.js +7 -0
- package/dist/services/optimization/detectors/index.js.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts +80 -0
- package/dist/services/optimization/detectors/perf-detector.d.ts.map +1 -0
- package/dist/services/optimization/detectors/perf-detector.js +451 -0
- package/dist/services/optimization/detectors/perf-detector.js.map +1 -0
- package/dist/services/optimization/index.d.ts +61 -0
- package/dist/services/optimization/index.d.ts.map +1 -0
- package/dist/services/optimization/index.js +69 -0
- package/dist/services/optimization/index.js.map +1 -0
- package/dist/services/optimization/optimization.service.d.ts +65 -0
- package/dist/services/optimization/optimization.service.d.ts.map +1 -0
- package/dist/services/optimization/optimization.service.js +511 -0
- package/dist/services/optimization/optimization.service.js.map +1 -0
- package/dist/services/optimization/optimization.types.d.ts +343 -0
- package/dist/services/optimization/optimization.types.d.ts.map +1 -0
- package/dist/services/optimization/optimization.types.js +8 -0
- package/dist/services/optimization/optimization.types.js.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts +87 -0
- package/dist/services/optimization/optimizers/code-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/code-optimizer.js +436 -0
- package/dist/services/optimization/optimizers/code-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/index.d.ts +7 -0
- package/dist/services/optimization/optimizers/index.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/index.js +7 -0
- package/dist/services/optimization/optimizers/index.js.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts +64 -0
- package/dist/services/optimization/optimizers/perf-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js +330 -0
- package/dist/services/optimization/optimizers/perf-optimizer.js.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts +82 -0
- package/dist/services/optimization/optimizers/refact-optimizer.d.ts.map +1 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js +354 -0
- package/dist/services/optimization/optimizers/refact-optimizer.js.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts +31 -0
- package/dist/services/optimization/patterns/anti-patterns.d.ts.map +1 -0
- package/dist/services/optimization/patterns/anti-patterns.js +501 -0
- package/dist/services/optimization/patterns/anti-patterns.js.map +1 -0
- package/dist/services/optimization/patterns/index.d.ts +5 -0
- package/dist/services/optimization/patterns/index.d.ts.map +1 -0
- package/dist/services/optimization/patterns/index.js +5 -0
- package/dist/services/optimization/patterns/index.js.map +1 -0
- package/dist/services/reporting/export/chart.exporter.d.ts +59 -0
- package/dist/services/reporting/export/chart.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/chart.exporter.js +350 -0
- package/dist/services/reporting/export/chart.exporter.js.map +1 -0
- package/dist/services/reporting/export/index.d.ts +9 -0
- package/dist/services/reporting/export/index.d.ts.map +1 -0
- package/dist/services/reporting/export/index.js +10 -0
- package/dist/services/reporting/export/index.js.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts +133 -0
- package/dist/services/reporting/export/pdf.exporter.d.ts.map +1 -0
- package/dist/services/reporting/export/pdf.exporter.js +270 -0
- package/dist/services/reporting/export/pdf.exporter.js.map +1 -0
- package/dist/services/reporting/history.service.d.ts +93 -0
- package/dist/services/reporting/history.service.d.ts.map +1 -0
- package/dist/services/reporting/history.service.js +285 -0
- package/dist/services/reporting/history.service.js.map +1 -0
- package/dist/services/reporting/index.d.ts +15 -0
- package/dist/services/reporting/index.d.ts.map +1 -0
- package/dist/services/reporting/index.js +16 -0
- package/dist/services/reporting/index.js.map +1 -0
- package/dist/services/reporting/report.service.d.ts +102 -0
- package/dist/services/reporting/report.service.d.ts.map +1 -0
- package/dist/services/reporting/report.service.js +240 -0
- package/dist/services/reporting/report.service.js.map +1 -0
- package/dist/services/reporting/reporting.types.d.ts +329 -0
- package/dist/services/reporting/reporting.types.d.ts.map +1 -0
- package/dist/services/reporting/reporting.types.js +8 -0
- package/dist/services/reporting/reporting.types.js.map +1 -0
- package/dist/services/reporting/templates/html.template.d.ts +81 -0
- package/dist/services/reporting/templates/html.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/html.template.js +741 -0
- package/dist/services/reporting/templates/html.template.js.map +1 -0
- package/dist/services/reporting/templates/json.template.d.ts +85 -0
- package/dist/services/reporting/templates/json.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/json.template.js +308 -0
- package/dist/services/reporting/templates/json.template.js.map +1 -0
- package/dist/services/reporting/templates/markdown.template.d.ts +69 -0
- package/dist/services/reporting/templates/markdown.template.d.ts.map +1 -0
- package/dist/services/reporting/templates/markdown.template.js +311 -0
- package/dist/services/reporting/templates/markdown.template.js.map +1 -0
- package/dist/services/reporting/trend-analyzer.d.ts +73 -0
- package/dist/services/reporting/trend-analyzer.d.ts.map +1 -0
- package/dist/services/reporting/trend-analyzer.js +291 -0
- package/dist/services/reporting/trend-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts +87 -0
- package/dist/services/review/analyzers/dependency-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/dependency-analyzer.js +458 -0
- package/dist/services/review/analyzers/dependency-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/index.d.ts +13 -0
- package/dist/services/review/analyzers/index.d.ts.map +1 -0
- package/dist/services/review/analyzers/index.js +13 -0
- package/dist/services/review/analyzers/index.js.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts +210 -0
- package/dist/services/review/analyzers/nestjs-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js +571 -0
- package/dist/services/review/analyzers/nestjs-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts +91 -0
- package/dist/services/review/analyzers/performance-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/performance-analyzer.js +589 -0
- package/dist/services/review/analyzers/performance-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts +96 -0
- package/dist/services/review/analyzers/security-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/security-analyzer.js +512 -0
- package/dist/services/review/analyzers/security-analyzer.js.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts +90 -0
- package/dist/services/review/analyzers/static-analyzer.d.ts.map +1 -0
- package/dist/services/review/analyzers/static-analyzer.js +423 -0
- package/dist/services/review/analyzers/static-analyzer.js.map +1 -0
- package/dist/services/review/fixers/auto-fixer.d.ts +94 -0
- package/dist/services/review/fixers/auto-fixer.d.ts.map +1 -0
- package/dist/services/review/fixers/auto-fixer.js +404 -0
- package/dist/services/review/fixers/auto-fixer.js.map +1 -0
- package/dist/services/review/fixers/index.d.ts +11 -0
- package/dist/services/review/fixers/index.d.ts.map +1 -0
- package/dist/services/review/fixers/index.js +11 -0
- package/dist/services/review/fixers/index.js.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts +100 -0
- package/dist/services/review/fixers/refactor-suggester.d.ts.map +1 -0
- package/dist/services/review/fixers/refactor-suggester.js +555 -0
- package/dist/services/review/fixers/refactor-suggester.js.map +1 -0
- package/dist/services/review/fixers/test-generator.d.ts +99 -0
- package/dist/services/review/fixers/test-generator.d.ts.map +1 -0
- package/dist/services/review/fixers/test-generator.js +458 -0
- package/dist/services/review/fixers/test-generator.js.map +1 -0
- package/dist/services/review/index.d.ts +14 -0
- package/dist/services/review/index.d.ts.map +1 -0
- package/dist/services/review/index.js +14 -0
- package/dist/services/review/index.js.map +1 -0
- package/dist/services/review/reporters/fix-reporter.d.ts +67 -0
- package/dist/services/review/reporters/fix-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/fix-reporter.js +437 -0
- package/dist/services/review/reporters/fix-reporter.js.map +1 -0
- package/dist/services/review/reporters/index.d.ts +10 -0
- package/dist/services/review/reporters/index.d.ts.map +1 -0
- package/dist/services/review/reporters/index.js +10 -0
- package/dist/services/review/reporters/index.js.map +1 -0
- package/dist/services/review/reporters/score-reporter.d.ts +84 -0
- package/dist/services/review/reporters/score-reporter.d.ts.map +1 -0
- package/dist/services/review/reporters/score-reporter.js +560 -0
- package/dist/services/review/reporters/score-reporter.js.map +1 -0
- package/dist/services/review/review.service.d.ts +129 -0
- package/dist/services/review/review.service.d.ts.map +1 -0
- package/dist/services/review/review.service.js +396 -0
- package/dist/services/review/review.service.js.map +1 -0
- package/dist/services/review/review.types.d.ts +443 -0
- package/dist/services/review/review.types.d.ts.map +1 -0
- package/dist/services/review/review.types.js +11 -0
- package/dist/services/review/review.types.js.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js +260 -0
- package/dist/services/scoring/dimensions/accessibility.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts +138 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js +713 -0
- package/dist/services/scoring/dimensions/backend-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js +747 -0
- package/dist/services/scoring/dimensions/business-logic.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts +142 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js +685 -0
- package/dist/services/scoring/dimensions/code-quality.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/index.d.ts +18 -0
- package/dist/services/scoring/dimensions/index.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/index.js +27 -0
- package/dist/services/scoring/dimensions/index.js.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts +125 -0
- package/dist/services/scoring/dimensions/performance.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js +615 -0
- package/dist/services/scoring/dimensions/performance.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts +53 -0
- package/dist/services/scoring/dimensions/security.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/security.analyzer.js +327 -0
- package/dist/services/scoring/dimensions/security.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts +77 -0
- package/dist/services/scoring/dimensions/seo.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js +502 -0
- package/dist/services/scoring/dimensions/seo.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts +106 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js +496 -0
- package/dist/services/scoring/dimensions/test-coverage.analyzer.js.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts +126 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.d.ts.map +1 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js +665 -0
- package/dist/services/scoring/dimensions/ui-ux.analyzer.js.map +1 -0
- package/dist/services/scoring/index.d.ts +10 -0
- package/dist/services/scoring/index.d.ts.map +1 -0
- package/dist/services/scoring/index.js +10 -0
- package/dist/services/scoring/index.js.map +1 -0
- package/dist/services/scoring/scoring-service.d.ts +222 -0
- package/dist/services/scoring/scoring-service.d.ts.map +1 -0
- package/dist/services/scoring/scoring-service.js +636 -0
- package/dist/services/scoring/scoring-service.js.map +1 -0
- package/package.json +13 -4
- package/templates/README.md +183 -0
- package/templates/nestjs/controller.spec.ts +203 -0
- package/templates/nestjs/e2e/api.e2e-spec.ts +451 -0
- package/templates/nestjs/e2e/auth.e2e-spec.ts +533 -0
- package/templates/nestjs/fixtures/test-module.ts +311 -0
- package/templates/nestjs/guard.spec.ts +314 -0
- package/templates/nestjs/interceptor.spec.ts +458 -0
- package/templates/nestjs/module.spec.ts +173 -0
- package/templates/nestjs/pipe.spec.ts +474 -0
- package/templates/nestjs/service.spec.ts +296 -0
- package/templates/rust/Cargo.toml +72 -0
- package/templates/rust/actix-controller.test.rs +114 -0
- package/templates/rust/axum-handler.test.rs +117 -0
- package/templates/rust/integration.test.rs +63 -0
- package/templates/rust/rocket-route.test.rs +106 -0
- package/templates/rust/unit.test.rs +38 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI/UX Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes UI/UX quality including responsive design, loading states,
|
|
5
|
+
* error handling UX, form validation feedback, and empty states.
|
|
6
|
+
*
|
|
7
|
+
* @module services/scoring/dimensions/ui-ux
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
10
|
+
import { join, extname } from 'node:path';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { CommandExecutor } from '../../../shared/utils/command-executor.js';
|
|
13
|
+
import { createLogger } from '../../../shared/utils/logger.js';
|
|
14
|
+
/**
|
|
15
|
+
* UI/UX Analyzer
|
|
16
|
+
*
|
|
17
|
+
* Evaluates UI/UX quality across multiple dimensions:
|
|
18
|
+
* - Responsive design (viewport meta, media queries)
|
|
19
|
+
* - Loading states (skeletons, spinners)
|
|
20
|
+
* - Error handling UX
|
|
21
|
+
* - Form validation feedback
|
|
22
|
+
* - Empty states
|
|
23
|
+
*/
|
|
24
|
+
export class UiUxAnalyzer {
|
|
25
|
+
/** Analyzer configuration */
|
|
26
|
+
config = {
|
|
27
|
+
dimension: 'ui-ux',
|
|
28
|
+
defaultWeight: 0.10,
|
|
29
|
+
estimatedDuration: 20000,
|
|
30
|
+
supportedFrameworks: ['Next.js', 'React', 'Vue', 'Nuxt', 'Svelte', 'Angular', 'Remix', 'SvelteKit', 'Astro', 'Gatsby'],
|
|
31
|
+
};
|
|
32
|
+
logger;
|
|
33
|
+
executor;
|
|
34
|
+
_checkResponsive;
|
|
35
|
+
_checkLoadingStates;
|
|
36
|
+
_checkErrorHandling;
|
|
37
|
+
_checkFormValidation;
|
|
38
|
+
_checkEmptyStates;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.logger = createLogger('UiUxAnalyzer');
|
|
41
|
+
this.executor = new CommandExecutor();
|
|
42
|
+
this._checkResponsive = options.checkResponsive ?? true;
|
|
43
|
+
this._checkLoadingStates = options.checkLoadingStates ?? true;
|
|
44
|
+
this._checkErrorHandling = options.checkErrorHandling ?? true;
|
|
45
|
+
this._checkFormValidation = options.checkFormValidation ?? true;
|
|
46
|
+
this._checkEmptyStates = options.checkEmptyStates ?? true;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the dimension this analyzer handles
|
|
50
|
+
*/
|
|
51
|
+
getDimension() {
|
|
52
|
+
return 'ui-ux';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the default weight for this dimension
|
|
56
|
+
*/
|
|
57
|
+
getWeight() {
|
|
58
|
+
return 0.10; // 10% weight in overall score
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Analyze UI/UX for a project
|
|
62
|
+
*
|
|
63
|
+
* @param projectPath - Path to the project root
|
|
64
|
+
* @param _framework - Detected framework (optional, for framework-specific analysis)
|
|
65
|
+
* @param _options - Scoring options (optional)
|
|
66
|
+
* @returns Dimension score with UI/UX metrics
|
|
67
|
+
*/
|
|
68
|
+
async analyze(projectPath, _framework, _options) {
|
|
69
|
+
const startTime = performance.now();
|
|
70
|
+
this.logger.info(`Analyzing UI/UX for: ${projectPath}`);
|
|
71
|
+
const issues = [];
|
|
72
|
+
const improvements = [];
|
|
73
|
+
try {
|
|
74
|
+
// Check responsive design
|
|
75
|
+
const responsiveMetrics = this._checkResponsive ? await this.checkResponsiveDesign(projectPath) : { hasResponsiveDesign: false, breakpointCount: 0 };
|
|
76
|
+
// Check loading states
|
|
77
|
+
const loadingMetrics = this._checkLoadingStates ? await this.checkLoadingStatesPatterns(projectPath) : { hasLoadingStates: false, loadingPatterns: new Set() };
|
|
78
|
+
// Check error handling
|
|
79
|
+
const errorMetrics = this._checkErrorHandling ? await this.checkErrorHandlingPatterns(projectPath) : { hasErrorHandling: false, errorPatterns: new Set() };
|
|
80
|
+
// Check form validation
|
|
81
|
+
const validationMetrics = this._checkFormValidation ? await this.checkFormValidationPatterns(projectPath) : { hasFormValidation: false, validationPatterns: new Set() };
|
|
82
|
+
// Check empty states
|
|
83
|
+
const emptyStateMetrics = this._checkEmptyStates ? await this.checkEmptyStatesPatterns(projectPath) : { hasEmptyStates: false, emptyStateCount: 0 };
|
|
84
|
+
const metrics = {
|
|
85
|
+
...responsiveMetrics,
|
|
86
|
+
...loadingMetrics,
|
|
87
|
+
...errorMetrics,
|
|
88
|
+
...validationMetrics,
|
|
89
|
+
...emptyStateMetrics,
|
|
90
|
+
};
|
|
91
|
+
// Build issues list
|
|
92
|
+
issues.push(...this.identifyUiUxIssues(metrics));
|
|
93
|
+
// Build improvements list
|
|
94
|
+
improvements.push(...this.generateUiUxImprovements(metrics));
|
|
95
|
+
// Calculate final score
|
|
96
|
+
const score = this.calculateUiUxScore(metrics);
|
|
97
|
+
const duration = Math.round(performance.now() - startTime);
|
|
98
|
+
return {
|
|
99
|
+
dimension: this.getDimension(),
|
|
100
|
+
score,
|
|
101
|
+
weight: this.getWeight(),
|
|
102
|
+
weightedScore: score * this.getWeight(),
|
|
103
|
+
issues,
|
|
104
|
+
improvements,
|
|
105
|
+
metadata: {
|
|
106
|
+
itemsChecked: 5,
|
|
107
|
+
itemsPassed: this.countPassedChecks(metrics),
|
|
108
|
+
metrics: {
|
|
109
|
+
hasResponsiveDesign: metrics.hasResponsiveDesign ? 1 : 0,
|
|
110
|
+
breakpointCount: metrics.breakpointCount,
|
|
111
|
+
hasLoadingStates: metrics.hasLoadingStates ? 1 : 0,
|
|
112
|
+
loadingPatternCount: metrics.loadingPatterns.size,
|
|
113
|
+
hasErrorHandling: metrics.hasErrorHandling ? 1 : 0,
|
|
114
|
+
errorPatternCount: metrics.errorPatterns.size,
|
|
115
|
+
hasFormValidation: metrics.hasFormValidation ? 1 : 0,
|
|
116
|
+
validationPatternCount: metrics.validationPatterns.size,
|
|
117
|
+
hasEmptyStates: metrics.hasEmptyStates ? 1 : 0,
|
|
118
|
+
emptyStateCount: metrics.emptyStateCount,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.logger.error('Error analyzing UI/UX', error);
|
|
125
|
+
issues.push({
|
|
126
|
+
severity: 'medium',
|
|
127
|
+
category: 'ux',
|
|
128
|
+
description: `Failed to analyze UI/UX: ${error instanceof Error ? error.message : String(error)}`,
|
|
129
|
+
fixable: false,
|
|
130
|
+
});
|
|
131
|
+
const duration = Math.round(performance.now() - startTime);
|
|
132
|
+
return {
|
|
133
|
+
dimension: this.getDimension(),
|
|
134
|
+
score: 50,
|
|
135
|
+
weight: this.getWeight(),
|
|
136
|
+
weightedScore: 50 * this.getWeight(),
|
|
137
|
+
issues,
|
|
138
|
+
improvements,
|
|
139
|
+
metadata: { error: String(error) },
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check for responsive design
|
|
145
|
+
*/
|
|
146
|
+
async checkResponsiveDesign(projectPath) {
|
|
147
|
+
let hasResponsiveDesign = false;
|
|
148
|
+
let breakpointCount = 0;
|
|
149
|
+
// Check for viewport meta tag
|
|
150
|
+
const indexHtml = join(projectPath, 'index.html');
|
|
151
|
+
if (existsSync(indexHtml)) {
|
|
152
|
+
try {
|
|
153
|
+
const content = await readFile(indexHtml, 'utf-8');
|
|
154
|
+
if (content.includes('viewport')) {
|
|
155
|
+
hasResponsiveDesign = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Ignore
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Check for media queries in CSS/SCSS files
|
|
163
|
+
const srcDir = join(projectPath, 'src');
|
|
164
|
+
const appDir = join(projectPath, 'app');
|
|
165
|
+
const dirsToCheck = [];
|
|
166
|
+
if (existsSync(srcDir))
|
|
167
|
+
dirsToCheck.push(srcDir);
|
|
168
|
+
if (existsSync(appDir))
|
|
169
|
+
dirsToCheck.push(appDir);
|
|
170
|
+
for (const dir of dirsToCheck) {
|
|
171
|
+
await this.scanDirectoryForMediaQueries(dir, (count) => {
|
|
172
|
+
if (count > 0) {
|
|
173
|
+
hasResponsiveDesign = true;
|
|
174
|
+
breakpointCount += count;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// Check Tailwind or other responsive utilities
|
|
179
|
+
try {
|
|
180
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
181
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
182
|
+
const pkg = JSON.parse(pkgContent);
|
|
183
|
+
if (pkg.devDependencies?.tailwindcss || pkg.dependencies?.tailwindcss) {
|
|
184
|
+
hasResponsiveDesign = true;
|
|
185
|
+
// Tailwind has default breakpoints
|
|
186
|
+
breakpointCount = Math.max(breakpointCount, 5); // sm, md, lg, xl, 2xl
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Ignore
|
|
191
|
+
}
|
|
192
|
+
return { hasResponsiveDesign, breakpointCount };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Scan directory for media queries
|
|
196
|
+
*/
|
|
197
|
+
async scanDirectoryForMediaQueries(dir, callback) {
|
|
198
|
+
try {
|
|
199
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
const fullPath = join(dir, entry.name);
|
|
202
|
+
if (entry.isDirectory()) {
|
|
203
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
204
|
+
await this.scanDirectoryForMediaQueries(fullPath, callback);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (entry.isFile()) {
|
|
208
|
+
const ext = extname(entry.name);
|
|
209
|
+
if (['.css', '.scss', '.sass', '.less'].includes(ext)) {
|
|
210
|
+
try {
|
|
211
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
212
|
+
const mediaQueryCount = (content.match(/@media/g) ?? []).length;
|
|
213
|
+
callback(mediaQueryCount);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Skip unreadable files
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Directory not accessible
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Check for loading states
|
|
228
|
+
*/
|
|
229
|
+
async checkLoadingStatesPatterns(projectPath) {
|
|
230
|
+
const loadingPatterns = new Set();
|
|
231
|
+
const srcDir = join(projectPath, 'src');
|
|
232
|
+
const appDir = join(projectPath, 'app');
|
|
233
|
+
const dirsToCheck = [];
|
|
234
|
+
if (existsSync(srcDir))
|
|
235
|
+
dirsToCheck.push(srcDir);
|
|
236
|
+
if (existsSync(appDir))
|
|
237
|
+
dirsToCheck.push(appDir);
|
|
238
|
+
// Loading patterns to detect
|
|
239
|
+
const patterns = new Map([
|
|
240
|
+
['skeleton', [/skeleton/i, /Skeleton/i]],
|
|
241
|
+
['spinner', [/spinner/i, /Spinner/i, /loading/i, /Loading/i]],
|
|
242
|
+
['progress', [/progress/i, /Progress/i]],
|
|
243
|
+
['inline', [/isLoading/i, /isPending/i, /pending/i]],
|
|
244
|
+
]);
|
|
245
|
+
for (const dir of dirsToCheck) {
|
|
246
|
+
await this.scanDirectoryForLoadingPatterns(dir, patterns, loadingPatterns);
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
hasLoadingStates: loadingPatterns.size > 0,
|
|
250
|
+
loadingPatterns,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Scan directory for loading patterns
|
|
255
|
+
*/
|
|
256
|
+
async scanDirectoryForLoadingPatterns(dir, patterns, foundPatterns) {
|
|
257
|
+
try {
|
|
258
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
259
|
+
for (const entry of entries) {
|
|
260
|
+
const fullPath = join(dir, entry.name);
|
|
261
|
+
if (entry.isDirectory()) {
|
|
262
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
263
|
+
await this.scanDirectoryForLoadingPatterns(fullPath, patterns, foundPatterns);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if (entry.isFile()) {
|
|
267
|
+
const ext = extname(entry.name);
|
|
268
|
+
if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
269
|
+
try {
|
|
270
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
271
|
+
for (const [patternType, regexList] of Array.from(patterns.entries())) {
|
|
272
|
+
for (const regex of regexList) {
|
|
273
|
+
if (regex.test(content)) {
|
|
274
|
+
foundPatterns.add(patternType);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Skip unreadable files
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// Directory not accessible
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Check for error handling
|
|
293
|
+
*/
|
|
294
|
+
async checkErrorHandlingPatterns(projectPath) {
|
|
295
|
+
const errorPatterns = new Set();
|
|
296
|
+
const srcDir = join(projectPath, 'src');
|
|
297
|
+
const appDir = join(projectPath, 'app');
|
|
298
|
+
const dirsToCheck = [];
|
|
299
|
+
if (existsSync(srcDir))
|
|
300
|
+
dirsToCheck.push(srcDir);
|
|
301
|
+
if (existsSync(appDir))
|
|
302
|
+
dirsToCheck.push(appDir);
|
|
303
|
+
// Error patterns to detect
|
|
304
|
+
const patterns = new Map([
|
|
305
|
+
['boundary', [/ErrorBoundary/i, /error-boundary/i]],
|
|
306
|
+
['toast', [/toast/i, /Toast/i, /notification/i]],
|
|
307
|
+
['inline', [/error.*message/i, /showError/i]],
|
|
308
|
+
['page', [/\b404\b/, /\b500\b/, /notfound/i, /error.*page/i]],
|
|
309
|
+
]);
|
|
310
|
+
for (const dir of dirsToCheck) {
|
|
311
|
+
await this.scanDirectoryForErrorPatterns(dir, patterns, errorPatterns);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
hasErrorHandling: errorPatterns.size > 0,
|
|
315
|
+
errorPatterns,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Scan directory for error patterns
|
|
320
|
+
*/
|
|
321
|
+
async scanDirectoryForErrorPatterns(dir, patterns, foundPatterns) {
|
|
322
|
+
try {
|
|
323
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
324
|
+
for (const entry of entries) {
|
|
325
|
+
const fullPath = join(dir, entry.name);
|
|
326
|
+
if (entry.isDirectory()) {
|
|
327
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
328
|
+
await this.scanDirectoryForErrorPatterns(fullPath, patterns, foundPatterns);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
else if (entry.isFile()) {
|
|
332
|
+
const ext = extname(entry.name);
|
|
333
|
+
if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
334
|
+
try {
|
|
335
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
336
|
+
for (const [patternType, regexList] of Array.from(patterns.entries())) {
|
|
337
|
+
for (const regex of regexList) {
|
|
338
|
+
if (regex.test(content)) {
|
|
339
|
+
foundPatterns.add(patternType);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Skip unreadable files
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
// Directory not accessible
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Check for form validation
|
|
358
|
+
*/
|
|
359
|
+
async checkFormValidationPatterns(projectPath) {
|
|
360
|
+
const validationPatterns = new Set();
|
|
361
|
+
const srcDir = join(projectPath, 'src');
|
|
362
|
+
const appDir = join(projectPath, 'app');
|
|
363
|
+
const dirsToCheck = [];
|
|
364
|
+
if (existsSync(srcDir))
|
|
365
|
+
dirsToCheck.push(srcDir);
|
|
366
|
+
if (existsSync(appDir))
|
|
367
|
+
dirsToCheck.push(appDir);
|
|
368
|
+
// Validation patterns to detect
|
|
369
|
+
const patterns = new Map([
|
|
370
|
+
['inline', [/error.*text/i, /validation.*message/i, /helperText/i]],
|
|
371
|
+
['onblur', [/onBlur/i, /blur.*validation/i]],
|
|
372
|
+
['onsubmit', [/onSubmit.*validation/i, /validate.*form/i]],
|
|
373
|
+
['realtime', [/onChange.*validation/i, /watch.*form/i]],
|
|
374
|
+
]);
|
|
375
|
+
for (const dir of dirsToCheck) {
|
|
376
|
+
await this.scanDirectoryForValidationPatterns(dir, patterns, validationPatterns);
|
|
377
|
+
}
|
|
378
|
+
// Check for React Hook Form or Formik
|
|
379
|
+
try {
|
|
380
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
381
|
+
const pkgContent = await readFile(pkgPath, 'utf-8');
|
|
382
|
+
const pkg = JSON.parse(pkgContent);
|
|
383
|
+
if (pkg.dependencies?.['react-hook-form'] || pkg.dependencies?.formik) {
|
|
384
|
+
validationPatterns.add('realtime');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// Ignore
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
hasFormValidation: validationPatterns.size > 0,
|
|
392
|
+
validationPatterns,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Scan directory for validation patterns
|
|
397
|
+
*/
|
|
398
|
+
async scanDirectoryForValidationPatterns(dir, patterns, foundPatterns) {
|
|
399
|
+
try {
|
|
400
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
401
|
+
for (const entry of entries) {
|
|
402
|
+
const fullPath = join(dir, entry.name);
|
|
403
|
+
if (entry.isDirectory()) {
|
|
404
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
405
|
+
await this.scanDirectoryForValidationPatterns(fullPath, patterns, foundPatterns);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else if (entry.isFile()) {
|
|
409
|
+
const ext = extname(entry.name);
|
|
410
|
+
if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
411
|
+
try {
|
|
412
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
413
|
+
for (const [patternType, regexList] of Array.from(patterns.entries())) {
|
|
414
|
+
for (const regex of regexList) {
|
|
415
|
+
if (regex.test(content)) {
|
|
416
|
+
foundPatterns.add(patternType);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
// Skip unreadable files
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
// Directory not accessible
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Check for empty states
|
|
435
|
+
*/
|
|
436
|
+
async checkEmptyStatesPatterns(projectPath) {
|
|
437
|
+
let emptyStateCount = 0;
|
|
438
|
+
let hasEmptyStates = false;
|
|
439
|
+
const srcDir = join(projectPath, 'src');
|
|
440
|
+
const appDir = join(projectPath, 'app');
|
|
441
|
+
const dirsToCheck = [];
|
|
442
|
+
if (existsSync(srcDir))
|
|
443
|
+
dirsToCheck.push(srcDir);
|
|
444
|
+
if (existsSync(appDir))
|
|
445
|
+
dirsToCheck.push(appDir);
|
|
446
|
+
// Empty state patterns
|
|
447
|
+
const patterns = [
|
|
448
|
+
/empty/i,
|
|
449
|
+
/no.*data/i,
|
|
450
|
+
/no.*results/i,
|
|
451
|
+
/nothing.*found/i,
|
|
452
|
+
/zero.*state/i,
|
|
453
|
+
];
|
|
454
|
+
for (const dir of dirsToCheck) {
|
|
455
|
+
await this.scanDirectoryForEmptyStates(dir, patterns, (count) => {
|
|
456
|
+
if (count > 0) {
|
|
457
|
+
hasEmptyStates = true;
|
|
458
|
+
emptyStateCount += count;
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
return { hasEmptyStates, emptyStateCount };
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Scan directory for empty states
|
|
466
|
+
*/
|
|
467
|
+
async scanDirectoryForEmptyStates(dir, patterns, callback) {
|
|
468
|
+
try {
|
|
469
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
470
|
+
for (const entry of entries) {
|
|
471
|
+
const fullPath = join(dir, entry.name);
|
|
472
|
+
if (entry.isDirectory()) {
|
|
473
|
+
if (!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(entry.name)) {
|
|
474
|
+
await this.scanDirectoryForEmptyStates(fullPath, patterns, callback);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else if (entry.isFile()) {
|
|
478
|
+
const ext = extname(entry.name);
|
|
479
|
+
if (['.tsx', '.jsx', '.ts', '.js'].includes(ext)) {
|
|
480
|
+
try {
|
|
481
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
482
|
+
let count = 0;
|
|
483
|
+
for (const pattern of patterns) {
|
|
484
|
+
count += (content.match(pattern) ?? []).length;
|
|
485
|
+
}
|
|
486
|
+
callback(count);
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
// Skip unreadable files
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
// Directory not accessible
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Identify UI/UX issues
|
|
501
|
+
*/
|
|
502
|
+
identifyUiUxIssues(metrics) {
|
|
503
|
+
const issues = [];
|
|
504
|
+
if (!metrics.hasResponsiveDesign) {
|
|
505
|
+
issues.push({
|
|
506
|
+
severity: 'high',
|
|
507
|
+
category: 'ux',
|
|
508
|
+
description: 'No responsive design detected',
|
|
509
|
+
fixable: true,
|
|
510
|
+
suggestion: 'Add responsive design with media queries or a responsive framework',
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
if (!metrics.hasLoadingStates) {
|
|
514
|
+
issues.push({
|
|
515
|
+
severity: 'medium',
|
|
516
|
+
category: 'ux',
|
|
517
|
+
description: 'No loading states detected',
|
|
518
|
+
fixable: true,
|
|
519
|
+
suggestion: 'Add loading indicators (skeletons, spinners) for async operations',
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (!metrics.hasErrorHandling) {
|
|
523
|
+
issues.push({
|
|
524
|
+
severity: 'high',
|
|
525
|
+
category: 'ux',
|
|
526
|
+
description: 'No error handling UX detected',
|
|
527
|
+
fixable: true,
|
|
528
|
+
suggestion: 'Implement error boundaries and user-friendly error messages',
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
if (!metrics.hasFormValidation) {
|
|
532
|
+
issues.push({
|
|
533
|
+
severity: 'medium',
|
|
534
|
+
category: 'ux',
|
|
535
|
+
description: 'No form validation feedback detected',
|
|
536
|
+
fixable: true,
|
|
537
|
+
suggestion: 'Add inline validation and error messages for form inputs',
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
if (!metrics.hasEmptyStates) {
|
|
541
|
+
issues.push({
|
|
542
|
+
severity: 'low',
|
|
543
|
+
category: 'ux',
|
|
544
|
+
description: 'No empty states detected',
|
|
545
|
+
fixable: true,
|
|
546
|
+
suggestion: 'Add empty states for lists, tables, and data displays',
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
return issues;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Generate UI/UX improvements
|
|
553
|
+
*/
|
|
554
|
+
generateUiUxImprovements(metrics) {
|
|
555
|
+
const improvements = [];
|
|
556
|
+
if (!metrics.hasResponsiveDesign) {
|
|
557
|
+
improvements.push({
|
|
558
|
+
type: 'refactor',
|
|
559
|
+
description: 'Implement responsive design for mobile and tablet devices',
|
|
560
|
+
effort: 'significant',
|
|
561
|
+
impact: 'high',
|
|
562
|
+
steps: [
|
|
563
|
+
'Add viewport meta tag',
|
|
564
|
+
'Use responsive units (rem, %, vw/vh)',
|
|
565
|
+
'Add media queries for breakpoints',
|
|
566
|
+
'Test on multiple devices',
|
|
567
|
+
],
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
if (!metrics.hasLoadingStates) {
|
|
571
|
+
improvements.push({
|
|
572
|
+
type: 'refactor',
|
|
573
|
+
description: 'Add loading states for better perceived performance',
|
|
574
|
+
effort: 'moderate',
|
|
575
|
+
impact: 'medium',
|
|
576
|
+
steps: [
|
|
577
|
+
'Use skeleton screens for content loading',
|
|
578
|
+
'Add spinners for actions',
|
|
579
|
+
'Consider optimistic UI updates',
|
|
580
|
+
],
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
if (!metrics.hasErrorHandling) {
|
|
584
|
+
improvements.push({
|
|
585
|
+
type: 'refactor',
|
|
586
|
+
description: 'Implement comprehensive error handling UX',
|
|
587
|
+
effort: 'moderate',
|
|
588
|
+
impact: 'high',
|
|
589
|
+
steps: [
|
|
590
|
+
'Add error boundaries for React components',
|
|
591
|
+
'Display user-friendly error messages',
|
|
592
|
+
'Add retry mechanisms for failed operations',
|
|
593
|
+
'Log errors for debugging',
|
|
594
|
+
],
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
if (!metrics.hasFormValidation) {
|
|
598
|
+
improvements.push({
|
|
599
|
+
type: 'refactor',
|
|
600
|
+
description: 'Add form validation with clear feedback',
|
|
601
|
+
effort: 'moderate',
|
|
602
|
+
impact: 'medium',
|
|
603
|
+
steps: [
|
|
604
|
+
'Validate on blur for inline feedback',
|
|
605
|
+
'Show error messages below inputs',
|
|
606
|
+
'Disable submit button until valid',
|
|
607
|
+
'Use HTML5 validation attributes',
|
|
608
|
+
],
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
if (!metrics.hasEmptyStates) {
|
|
612
|
+
improvements.push({
|
|
613
|
+
type: 'refactor',
|
|
614
|
+
description: 'Add empty states for better UX',
|
|
615
|
+
effort: 'quick',
|
|
616
|
+
impact: 'low',
|
|
617
|
+
steps: [
|
|
618
|
+
'Create EmptyState component',
|
|
619
|
+
'Add illustrations or icons',
|
|
620
|
+
'Include clear messaging and actions',
|
|
621
|
+
],
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
return improvements;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Calculate UI/UX score
|
|
628
|
+
*/
|
|
629
|
+
calculateUiUxScore(metrics) {
|
|
630
|
+
let score = 0;
|
|
631
|
+
if (metrics.hasResponsiveDesign)
|
|
632
|
+
score += 25;
|
|
633
|
+
if (metrics.hasLoadingStates)
|
|
634
|
+
score += 20;
|
|
635
|
+
if (metrics.hasErrorHandling)
|
|
636
|
+
score += 25;
|
|
637
|
+
if (metrics.hasFormValidation)
|
|
638
|
+
score += 20;
|
|
639
|
+
if (metrics.hasEmptyStates)
|
|
640
|
+
score += 10;
|
|
641
|
+
return score;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Count passed checks for metadata
|
|
645
|
+
*/
|
|
646
|
+
countPassedChecks(metrics) {
|
|
647
|
+
let count = 0;
|
|
648
|
+
if (metrics.hasResponsiveDesign)
|
|
649
|
+
count++;
|
|
650
|
+
if (metrics.hasLoadingStates)
|
|
651
|
+
count++;
|
|
652
|
+
if (metrics.hasErrorHandling)
|
|
653
|
+
count++;
|
|
654
|
+
if (metrics.hasFormValidation)
|
|
655
|
+
count++;
|
|
656
|
+
if (metrics.hasEmptyStates)
|
|
657
|
+
count++;
|
|
658
|
+
return count;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Default analyzer instance
|
|
663
|
+
*/
|
|
664
|
+
export const uiUxAnalyzer = new UiUxAnalyzer();
|
|
665
|
+
//# sourceMappingURL=ui-ux.analyzer.js.map
|