- Add 26 documentation files including test reports, deployment guides, and troubleshooting documentation - Include 3 CSV data files for trainer imports and user registration tracking - Add 43 JavaScript test files covering mobile optimization, Safari compatibility, and E2E testing - Include 18 PHP utility files for debugging, geocoding, and data analysis - Add 12 shell scripts for deployment verification, user management, and database operations - Update .gitignore with whitelist patterns for development files, documentation, and CSV data 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
214 lines
No EOL
7.5 KiB
JavaScript
214 lines
No EOL
7.5 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
class ResponsiveAnalyzer {
|
||
constructor() {
|
||
this.breakpoints = new Map();
|
||
this.responsivePatterns = [];
|
||
this.issues = [];
|
||
}
|
||
|
||
analyzeResponsiveDesign() {
|
||
console.log('📱 RESPONSIVE DESIGN ANALYSIS');
|
||
console.log('='.repeat(50));
|
||
|
||
const cssFiles = [
|
||
'./assets/css/hvac-common.css',
|
||
'./assets/css/hvac-dashboard.css',
|
||
'./assets/css/hvac-registration.css',
|
||
'./assets/css/hvac-certificates.css',
|
||
'./assets/css/hvac-email-attendees.css',
|
||
'./assets/css/hvac-event-summary.css',
|
||
'./assets/css/hvac-attendee-profile.css',
|
||
'./assets/css/hvac-mobile-nav.css'
|
||
];
|
||
|
||
cssFiles.forEach(file => {
|
||
if (fs.existsSync(file)) {
|
||
this.analyzeFile(file);
|
||
}
|
||
});
|
||
|
||
this.generateResponsiveReport();
|
||
}
|
||
|
||
analyzeFile(filePath) {
|
||
const content = fs.readFileSync(filePath, 'utf8');
|
||
const fileName = path.basename(filePath);
|
||
|
||
console.log(`\n🔍 ${fileName}:`);
|
||
|
||
// Extract media queries
|
||
const mediaQueries = this.extractMediaQueries(content);
|
||
if (mediaQueries.length > 0) {
|
||
console.log(` 📱 ${mediaQueries.length} media queries found`);
|
||
mediaQueries.forEach(mq => {
|
||
console.log(` ${mq.condition} - ${mq.rules} rules`);
|
||
|
||
// Track breakpoint usage
|
||
const breakpoint = mq.condition;
|
||
if (this.breakpoints.has(breakpoint)) {
|
||
this.breakpoints.set(breakpoint, this.breakpoints.get(breakpoint) + 1);
|
||
} else {
|
||
this.breakpoints.set(breakpoint, 1);
|
||
}
|
||
});
|
||
} else {
|
||
console.log(` ⚠️ No media queries`);
|
||
this.issues.push({
|
||
file: fileName,
|
||
issue: 'No responsive design',
|
||
severity: 'medium'
|
||
});
|
||
}
|
||
|
||
// Check for mobile-first vs desktop-first approach
|
||
const mobileFirst = content.includes('min-width');
|
||
const desktopFirst = content.includes('max-width');
|
||
|
||
if (mobileFirst && desktopFirst) {
|
||
console.log(` 📱 Mixed approach (both min-width and max-width)`);
|
||
} else if (mobileFirst) {
|
||
console.log(` 📱 Mobile-first approach (min-width)`);
|
||
} else if (desktopFirst) {
|
||
console.log(` 🖥️ Desktop-first approach (max-width)`);
|
||
}
|
||
|
||
// Check for flexible units
|
||
const flexibleUnits = {
|
||
'rem': (content.match(/\d+(\.\d+)?rem/g) || []).length,
|
||
'em': (content.match(/\d+(\.\d+)?em/g) || []).length,
|
||
'%': (content.match(/\d+(\.\d+)?%/g) || []).length,
|
||
'vw': (content.match(/\d+(\.\d+)?vw/g) || []).length,
|
||
'vh': (content.match(/\d+(\.\d+)?vh/g) || []).length,
|
||
'px': (content.match(/\d+px/g) || []).length
|
||
};
|
||
|
||
console.log(` 📏 Units usage:`, flexibleUnits);
|
||
|
||
// Check for responsive patterns
|
||
this.checkResponsivePatterns(content, fileName);
|
||
}
|
||
|
||
extractMediaQueries(content) {
|
||
const mediaRegex = /@media[^{]*\{([^{}]*\{[^}]*\}[^{}]*)*\}/g;
|
||
const queries = [];
|
||
let match;
|
||
|
||
while ((match = mediaRegex.exec(content)) !== null) {
|
||
const fullQuery = match[0];
|
||
const condition = fullQuery.match(/@media[^{]*/)[0];
|
||
const body = fullQuery.slice(condition.length);
|
||
const rules = (body.match(/[^{}]+\{[^}]*\}/g) || []).length;
|
||
|
||
queries.push({
|
||
condition: condition.trim(),
|
||
body: body,
|
||
rules: rules
|
||
});
|
||
}
|
||
|
||
return queries;
|
||
}
|
||
|
||
checkResponsivePatterns(content, fileName) {
|
||
const patterns = {
|
||
'Flexible Grid': /display:\s*(grid|flex)/g,
|
||
'Responsive Images': /max-width:\s*100%/g,
|
||
'Fluid Typography': /font-size:\s*calc\(/g,
|
||
'Container Queries': /@container/g,
|
||
'Responsive Spacing': /margin|padding.*clamp|margin|padding.*min|margin|padding.*max/g,
|
||
'Responsive Tables': /overflow-x:\s*auto/g
|
||
};
|
||
|
||
Object.entries(patterns).forEach(([pattern, regex]) => {
|
||
const matches = content.match(regex);
|
||
if (matches) {
|
||
console.log(` ✅ ${pattern}: ${matches.length} instances`);
|
||
this.responsivePatterns.push({
|
||
file: fileName,
|
||
pattern: pattern,
|
||
count: matches.length
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
generateResponsiveReport() {
|
||
console.log('\n' + '='.repeat(80));
|
||
console.log('📋 RESPONSIVE DESIGN REPORT');
|
||
console.log('='.repeat(80));
|
||
|
||
// Breakpoint analysis
|
||
console.log('\n📱 BREAKPOINT USAGE:');
|
||
const sortedBreakpoints = Array.from(this.breakpoints.entries())
|
||
.sort(([,a], [,b]) => b - a);
|
||
|
||
sortedBreakpoints.forEach(([breakpoint, count]) => {
|
||
console.log(` ${breakpoint} - Used ${count} times`);
|
||
});
|
||
|
||
// Common breakpoints check
|
||
const standardBreakpoints = [
|
||
'(max-width: 768px)',
|
||
'(max-width: 480px)',
|
||
'(max-width: 1024px)',
|
||
'(min-width: 768px)',
|
||
'(min-width: 1024px)'
|
||
];
|
||
|
||
console.log('\n📐 BREAKPOINT CONSISTENCY:');
|
||
standardBreakpoints.forEach(bp => {
|
||
if (this.breakpoints.has(bp)) {
|
||
console.log(` ✅ ${bp} - Standard breakpoint used`);
|
||
} else {
|
||
console.log(` ⚠️ ${bp} - Standard breakpoint not used`);
|
||
}
|
||
});
|
||
|
||
// Responsive patterns summary
|
||
console.log('\n🎨 RESPONSIVE PATTERNS:');
|
||
const patternSummary = new Map();
|
||
this.responsivePatterns.forEach(p => {
|
||
if (patternSummary.has(p.pattern)) {
|
||
patternSummary.set(p.pattern, patternSummary.get(p.pattern) + p.count);
|
||
} else {
|
||
patternSummary.set(p.pattern, p.count);
|
||
}
|
||
});
|
||
|
||
Array.from(patternSummary.entries()).forEach(([pattern, count]) => {
|
||
console.log(` ✅ ${pattern}: ${count} total uses across files`);
|
||
});
|
||
|
||
// Issues summary
|
||
if (this.issues.length > 0) {
|
||
console.log('\n⚠️ RESPONSIVE ISSUES:');
|
||
this.issues.forEach((issue, i) => {
|
||
console.log(` ${i + 1}. ${issue.file}: ${issue.issue}`);
|
||
});
|
||
}
|
||
|
||
// Recommendations
|
||
console.log('\n💡 RESPONSIVE RECOMMENDATIONS:');
|
||
|
||
if (!this.breakpoints.has('(max-width: 320px)')) {
|
||
console.log(' • Consider adding extra-small device support (320px)');
|
||
}
|
||
|
||
if (!patternSummary.has('Fluid Typography')) {
|
||
console.log(' • Consider implementing fluid typography with clamp()');
|
||
}
|
||
|
||
if (!patternSummary.has('Container Queries')) {
|
||
console.log(' • Consider using container queries for component-based responsive design');
|
||
}
|
||
|
||
console.log(' • Test on actual devices, not just browser resize');
|
||
console.log(' • Use relative units (rem, em, %) for better scalability');
|
||
console.log(' • Implement progressive enhancement approach');
|
||
}
|
||
}
|
||
|
||
const analyzer = new ResponsiveAnalyzer();
|
||
analyzer.analyzeResponsiveDesign(); |