feat: add advanced analytics and territory management system
- Add comprehensive analytics components with export functionality - Implement territory management with manager performance tracking - Add seatmap components for venue layout management - Create customer management features with modal interface - Add advanced hooks for dashboard flags and territory data - Implement seat selection and venue management utilities - Add type definitions for ticketing and seatmap systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
196
reactrebuild0825/scripts/validate-theme.js
Executable file
196
reactrebuild0825/scripts/validate-theme.js
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Theme Validation Script
|
||||
* Ensures no hardcoded colors are used in the codebase
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
// Color patterns to detect
|
||||
const FORBIDDEN_PATTERNS = [
|
||||
// Hardcoded hex colors
|
||||
/#[0-9a-fA-F]{3,6}/g,
|
||||
|
||||
// Tailwind color classes we don't want
|
||||
/\b(slate|gray|zinc|neutral|stone)-\d+/g,
|
||||
/\b(red|green|blue|yellow|purple|pink|indigo|cyan|teal|orange|amber|lime|emerald|sky|violet|fuchsia|rose)-\d+/g,
|
||||
|
||||
// Generic color names in class names
|
||||
/\b(text|bg|border)-(white|black)\b/g,
|
||||
|
||||
// RGB/RGBA functions
|
||||
/rgba?\s*\([^)]+\)/g,
|
||||
|
||||
// HSL functions
|
||||
/hsla?\s*\([^)]+\)/g,
|
||||
|
||||
// Gradient utilities with hardcoded colors
|
||||
/\bfrom-(slate|gray|white|black|red|green|blue|yellow|purple|pink|indigo|cyan|teal|orange|amber|lime|emerald|sky|violet|fuchsia|rose)-\d+/g,
|
||||
/\bto-(slate|gray|white|black|red|green|blue|yellow|purple|pink|indigo|cyan|teal|orange|amber|lime|emerald|sky|violet|fuchsia|rose)-\d+/g,
|
||||
];
|
||||
|
||||
// Files to check
|
||||
const EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js', '.css'];
|
||||
const EXCLUDED_DIRS = ['node_modules', '.git', 'dist', 'build'];
|
||||
const EXCLUDED_FILES = [
|
||||
'tailwind.config.js', // Allow CSS variables in config
|
||||
'tokens.css', // Allow generated CSS variables
|
||||
'validate-theme.js', // Allow this script itself
|
||||
];
|
||||
|
||||
// Allowed exceptions (for documentation, comments, etc.)
|
||||
const ALLOWED_EXCEPTIONS = [
|
||||
// Comments about colors
|
||||
/\/\*.*?(#[0-9a-fA-F]{3,6}|slate-\d+).*?\*\//g,
|
||||
/\/\/.*?(#[0-9a-fA-F]{3,6}|slate-\d+)/g,
|
||||
|
||||
// String literals (not class names)
|
||||
/"[^"]*?(#[0-9a-fA-F]{3,6}|slate-\d+)[^"]*?"/g,
|
||||
/'[^']*?(#[0-9a-fA-F]{3,6}|slate-\d+)[^']*?'/g,
|
||||
|
||||
// Console.log and similar
|
||||
/console\.(log|warn|error).*?(#[0-9a-fA-F]{3,6}|slate-\d+)/g,
|
||||
];
|
||||
|
||||
function getAllFiles(dir, extensions = EXTENSIONS) {
|
||||
let results = [];
|
||||
const list = fs.readdirSync(dir);
|
||||
|
||||
list.forEach(file => {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
if (!EXCLUDED_DIRS.includes(file)) {
|
||||
results = results.concat(getAllFiles(filePath, extensions));
|
||||
}
|
||||
} else {
|
||||
const ext = path.extname(file);
|
||||
const basename = path.basename(file);
|
||||
|
||||
if (extensions.includes(ext) && !EXCLUDED_FILES.includes(basename)) {
|
||||
results.push(filePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function removeAllowedExceptions(content) {
|
||||
let cleaned = content;
|
||||
|
||||
ALLOWED_EXCEPTIONS.forEach(pattern => {
|
||||
cleaned = cleaned.replace(pattern, '');
|
||||
});
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
function validateFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const cleanedContent = removeAllowedExceptions(content);
|
||||
const violations = [];
|
||||
|
||||
FORBIDDEN_PATTERNS.forEach(pattern => {
|
||||
const matches = [...cleanedContent.matchAll(pattern)];
|
||||
|
||||
matches.forEach(match => {
|
||||
const lineNumber = content.substring(0, match.index).split('\n').length;
|
||||
violations.push({
|
||||
file: filePath,
|
||||
line: lineNumber,
|
||||
match: match[0],
|
||||
pattern: pattern.toString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log('🎨 Validating theme system...\n');
|
||||
|
||||
const projectRoot = path.join(__dirname, '..');
|
||||
const srcDir = path.join(projectRoot, 'src');
|
||||
const files = getAllFiles(srcDir);
|
||||
|
||||
console.log(`Checking ${files.length} files for hardcoded colors...\n`);
|
||||
|
||||
let totalViolations = 0;
|
||||
const violationsByFile = {};
|
||||
|
||||
files.forEach(file => {
|
||||
const violations = validateFile(file);
|
||||
|
||||
if (violations.length > 0) {
|
||||
violationsByFile[file] = violations;
|
||||
totalViolations += violations.length;
|
||||
}
|
||||
});
|
||||
|
||||
if (totalViolations === 0) {
|
||||
console.log('✅ No hardcoded colors found! Theme system is clean.\n');
|
||||
|
||||
// Additional check: ensure semantic classes are being used
|
||||
console.log('🔍 Checking for proper semantic token usage...\n');
|
||||
|
||||
const semanticPatterns = [
|
||||
/\btext-text-(primary|secondary|muted|inverse|disabled)\b/g,
|
||||
/\bbg-bg-(primary|secondary|tertiary|elevated|overlay)\b/g,
|
||||
/\bborder-border\b/g,
|
||||
/\baccent-(primary|secondary|gold)-\d+/g,
|
||||
];
|
||||
|
||||
let semanticUsageCount = 0;
|
||||
|
||||
files.forEach(file => {
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
semanticPatterns.forEach(pattern => {
|
||||
const matches = content.match(pattern);
|
||||
if (matches) {
|
||||
semanticUsageCount += matches.length;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`✅ Found ${semanticUsageCount} instances of semantic token usage.\n`);
|
||||
|
||||
return process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`❌ Found ${totalViolations} hardcoded color violations:\n`);
|
||||
|
||||
Object.entries(violationsByFile).forEach(([file, violations]) => {
|
||||
console.log(`📄 ${file}:`);
|
||||
|
||||
violations.forEach(violation => {
|
||||
console.log(` Line ${violation.line}: "${violation.match}"`);
|
||||
});
|
||||
|
||||
console.log('');
|
||||
});
|
||||
|
||||
console.log('🚨 VALIDATION FAILED!\n');
|
||||
console.log('Please replace hardcoded colors with semantic tokens from the design system.');
|
||||
console.log('See THEMING.md for guidance on proper token usage.\n');
|
||||
|
||||
console.log('Common replacements:');
|
||||
console.log(' text-slate-900 → text-text-primary');
|
||||
console.log(' text-slate-600 → text-text-secondary');
|
||||
console.log(' bg-white → bg-bg-primary');
|
||||
console.log(' border-slate-200 → border-border');
|
||||
console.log(' #3b82f6 → Use accent-primary-500 or similar\n');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user