2024-11-28 21:18:59 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>SheetSense Analyzer</title>
|
2024-12-03 15:17:00 +00:00
|
|
|
<!-- Google tag (gtag.js) -->
|
|
|
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1TJSQFTRJF"></script>
|
|
|
|
<script>
|
|
|
|
window.dataLayer = window.dataLayer || [];
|
|
|
|
function gtag(){dataLayer.push(arguments);}
|
|
|
|
gtag('js', new Date());
|
|
|
|
|
|
|
|
gtag('config', 'G-1TJSQFTRJF');
|
|
|
|
</script>
|
2024-11-28 21:18:59 +00:00
|
|
|
<script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js"></script>
|
|
|
|
<script src="https://unpkg.com/sheetsense/dist/sheetsense.browser.js"></script>
|
|
|
|
<style>
|
|
|
|
body {
|
|
|
|
font-family: system-ui, -apple-system, sans-serif;
|
|
|
|
max-width: 800px;
|
|
|
|
margin: 0 auto;
|
|
|
|
padding: 20px;
|
|
|
|
background: #f5f5f5;
|
|
|
|
}
|
2024-12-03 15:17:00 +00:00
|
|
|
.upload-container, .demo-container {
|
2024-11-28 21:18:59 +00:00
|
|
|
background: white;
|
2024-12-03 15:17:00 +00:00
|
|
|
padding: 0.50rem 2rem;
|
2024-11-28 21:18:59 +00:00
|
|
|
border-radius: 8px;
|
|
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
|
|
margin-bottom: 2rem;
|
|
|
|
}
|
2024-12-03 15:17:00 +00:00
|
|
|
.demo-grid {
|
|
|
|
display: grid;
|
|
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
|
|
gap: 1rem;
|
|
|
|
margin-top: 1rem;
|
|
|
|
}
|
|
|
|
.demo-card {
|
|
|
|
border: 1px solid #e5e7eb;
|
|
|
|
border-radius: 8px;
|
|
|
|
padding: 1rem;
|
|
|
|
background: #fff;
|
|
|
|
}
|
|
|
|
.demo-card h3 {
|
|
|
|
margin-top: 0;
|
|
|
|
color: #1f2937;
|
|
|
|
border-bottom: 2px solid #e5e7eb;
|
|
|
|
padding-bottom: 0.5rem;
|
|
|
|
}
|
|
|
|
.demo-button {
|
|
|
|
display: block;
|
|
|
|
width: 100%;
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
text-align: left;
|
|
|
|
background: none;
|
|
|
|
border: none;
|
|
|
|
border-bottom: 1px solid #e5e7eb;
|
|
|
|
cursor: pointer;
|
|
|
|
color: #4b5563;
|
|
|
|
font-size: 0.875rem;
|
|
|
|
}
|
|
|
|
.demo-button:last-child {
|
|
|
|
border-bottom: none;
|
|
|
|
}
|
|
|
|
.demo-button:hover {
|
|
|
|
background: #f9fafb;
|
|
|
|
}
|
2024-11-28 21:18:59 +00:00
|
|
|
#drop-zone {
|
|
|
|
border: 2px dashed #ccc;
|
|
|
|
border-radius: 4px;
|
|
|
|
padding: 2rem;
|
|
|
|
margin: 1rem 0;
|
|
|
|
cursor: pointer;
|
|
|
|
}
|
|
|
|
#drop-zone.dragover {
|
|
|
|
border-color: #0066cc;
|
|
|
|
background: #f0f7ff;
|
|
|
|
}
|
|
|
|
.results {
|
|
|
|
background: white;
|
|
|
|
padding: 1rem;
|
|
|
|
border-radius: 8px;
|
|
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
|
|
}
|
|
|
|
.issue {
|
|
|
|
margin: 1rem 0;
|
|
|
|
padding: 1rem;
|
|
|
|
border-radius: 4px;
|
|
|
|
}
|
2024-12-03 15:17:00 +00:00
|
|
|
.error { background: #fff5f5; border-left: 4px solid #dc2626; }
|
|
|
|
.warning { background: #fffbeb; border-left: 4px solid #d97706; }
|
|
|
|
.info { background: #f0f9ff; border-left: 4px solid #0284c7; }
|
|
|
|
.hidden { display: none; }
|
|
|
|
.loading {
|
2024-11-28 21:18:59 +00:00
|
|
|
text-align: center;
|
2024-12-03 15:17:00 +00:00
|
|
|
padding: 1rem;
|
2024-11-28 21:18:59 +00:00
|
|
|
color: #666;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div class="upload-container">
|
2024-12-03 15:17:00 +00:00
|
|
|
<h2>SheetSense Analyzer</h2>
|
|
|
|
<p>Excel workbook validation engine that detects quality issues, ensures consistency, and makes your spreadsheets bulletproof.</p>
|
2024-11-28 21:18:59 +00:00
|
|
|
<div id="drop-zone">
|
2024-12-03 15:17:00 +00:00
|
|
|
<p>Drop your Excel (xls or xlsx) file here or click to choose</p>
|
2024-11-28 21:18:59 +00:00
|
|
|
<input type="file" id="file-input" accept=".xlsx,.xls" class="hidden">
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2024-12-03 15:17:00 +00:00
|
|
|
|
|
|
|
<div class="demo-container">
|
|
|
|
<h2>Test Files</h2>
|
|
|
|
<div class="test-files-list">
|
|
|
|
<div class="test-files-category">
|
|
|
|
<div class="category-header">Circular Reference Tests</div>
|
|
|
|
<button class="demo-button" data-file="test_files/circular_reference/direct_circular.xlsx">
|
|
|
|
Direct Circular Reference Test
|
|
|
|
</button>
|
|
|
|
<button class="demo-button" data-file="test_files/circular_reference/indirect_circular.xlsx">
|
|
|
|
Indirect Circular Reference Test
|
|
|
|
</button>
|
|
|
|
<button class="demo-button" data-file="test_files/circular_reference/valid_cross_reference.xlsx">
|
|
|
|
Valid Cross Reference Test
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="test-files-category">
|
|
|
|
<div class="category-header">Data Quality Tests</div>
|
|
|
|
<button class="demo-button" data-file="test_files/data_quality/data_quality_issues.xlsx">
|
|
|
|
Data Quality Issues Test
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="test-files-category">
|
|
|
|
<div class="category-header">Formula Errors</div>
|
|
|
|
<button class="demo-button" data-file="test_files/formula_errors/formula_errors.xlsx">
|
|
|
|
Formula Errors Test
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="test-files-category">
|
|
|
|
<div class="category-header">Hidden Content</div>
|
|
|
|
<button class="demo-button" data-file="test_files/hidden_cell/hidden_cells.xlsx">
|
|
|
|
Hidden Cell
|
|
|
|
</button>
|
|
|
|
<button class="demo-button" data-file="test_files/hidden_cell/hidden_rows_columns.xlsx">
|
|
|
|
Hidden rows and columns
|
|
|
|
</button>
|
|
|
|
<button class="demo-button" data-file="test_files/hidden_cell/hidden_cells.xlsx">
|
|
|
|
Consecutive hidden rows
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2024-11-28 21:18:59 +00:00
|
|
|
<div id="results" class="results hidden">
|
|
|
|
<h2>Analysis Results</h2>
|
|
|
|
<div class="metadata">
|
|
|
|
<h3>Workbook Metadata</h3>
|
|
|
|
<div id="metadata-content"></div>
|
|
|
|
</div>
|
|
|
|
<div id="issues-container">
|
|
|
|
<h3>Issues Found</h3>
|
|
|
|
<div id="issues-content"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2024-12-03 15:17:00 +00:00
|
|
|
<div class="footer">
|
2024-11-28 21:18:59 +00:00
|
|
|
<p>
|
2024-12-03 15:17:00 +00:00
|
|
|
<a href="https://ezy.ovh/bmicX" target="_blank">View on GitHub</a> |
|
|
|
|
Created by <a href="https://ezy.ovh/oEaFv" target="_blank">@asadbek064</a>
|
|
|
|
<a href="https://mylinx.cc/asad" target="_blank">mylinx.cc/asad</a>
|
2024-11-28 21:18:59 +00:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
2024-12-03 15:17:00 +00:00
|
|
|
|
2024-11-28 21:18:59 +00:00
|
|
|
<script>
|
|
|
|
const dropZone = document.getElementById('drop-zone');
|
|
|
|
const fileInput = document.getElementById('file-input');
|
|
|
|
const results = document.getElementById('results');
|
|
|
|
const metadataContent = document.getElementById('metadata-content');
|
|
|
|
const issuesContent = document.getElementById('issues-content');
|
|
|
|
|
2024-12-03 15:17:00 +00:00
|
|
|
// Demo button handlers
|
|
|
|
document.querySelectorAll('.demo-button').forEach(button => {
|
|
|
|
button.addEventListener('click', async () => {
|
|
|
|
const filePath = button.dataset.file;
|
|
|
|
results.classList.add('hidden');
|
|
|
|
|
|
|
|
// Show loading state
|
|
|
|
const loadingDiv = document.createElement('div');
|
|
|
|
loadingDiv.className = 'loading';
|
|
|
|
loadingDiv.innerHTML = `Loading ${filePath}...`;
|
|
|
|
button.parentNode.appendChild(loadingDiv);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Use relative path from the root of your Cloudflare Pages deployment
|
|
|
|
const response = await fetch(`/${filePath}`, {
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/octet-stream'
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
throw new Error(`Failed to load file: ${filePath} (${response.status} ${response.statusText})`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
|
|
const data = new Uint8Array(arrayBuffer);
|
|
|
|
|
|
|
|
const workbook = XLSX.read(data, {
|
|
|
|
type: 'array',
|
|
|
|
cellFormula: true,
|
|
|
|
cellNF: true,
|
|
|
|
cellText: true,
|
|
|
|
cellStyles: true,
|
|
|
|
cellDates: true,
|
|
|
|
raw: true
|
|
|
|
});
|
|
|
|
|
|
|
|
const analyzer = new SheetSense.ExcelAnalyzer(workbook);
|
|
|
|
const analysis = analyzer.analyze();
|
|
|
|
displayResults(analysis);
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error loading test file:', error);
|
|
|
|
issuesContent.innerHTML = `
|
|
|
|
<div class="issue error">
|
|
|
|
<strong>Error loading test file</strong>
|
|
|
|
<p>${error.message}</p>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
results.classList.remove('hidden');
|
|
|
|
} finally {
|
|
|
|
loadingDiv.remove();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Rest of the JavaScript code remains the same
|
2024-11-28 21:18:59 +00:00
|
|
|
dropZone.addEventListener('dragover', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
dropZone.classList.add('dragover');
|
|
|
|
});
|
|
|
|
|
|
|
|
dropZone.addEventListener('dragleave', () => {
|
|
|
|
dropZone.classList.remove('dragover');
|
|
|
|
});
|
|
|
|
|
|
|
|
dropZone.addEventListener('drop', (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
dropZone.classList.remove('dragover');
|
|
|
|
const file = e.dataTransfer.files[0];
|
|
|
|
handleFile(file);
|
|
|
|
});
|
|
|
|
|
|
|
|
dropZone.addEventListener('click', () => {
|
|
|
|
fileInput.click();
|
|
|
|
});
|
|
|
|
|
|
|
|
fileInput.addEventListener('change', (e) => {
|
|
|
|
const file = e.target.files[0];
|
|
|
|
handleFile(file);
|
|
|
|
});
|
|
|
|
|
|
|
|
function handleFile(file) {
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = (e) => {
|
|
|
|
const data = new Uint8Array(e.target.result);
|
2024-12-03 15:17:00 +00:00
|
|
|
const workbook = XLSX.read(data, {
|
|
|
|
type: 'buffer',
|
|
|
|
cellFormula: true,
|
|
|
|
cellNF: true,
|
|
|
|
cellText: true,
|
|
|
|
cellStyles: true,
|
|
|
|
cellDates: true,
|
|
|
|
raw: true
|
|
|
|
});
|
2024-11-28 21:18:59 +00:00
|
|
|
|
|
|
|
const analyzer = new SheetSense.ExcelAnalyzer(workbook);
|
|
|
|
const analysis = analyzer.analyze();
|
|
|
|
displayResults(analysis);
|
|
|
|
};
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
function displayResults(analysis) {
|
|
|
|
results.classList.remove('hidden');
|
|
|
|
|
|
|
|
metadataContent.innerHTML = `
|
|
|
|
<p>Sheets: ${analysis.metadata.sheetCount}</p>
|
|
|
|
<p>Formulas: ${analysis.metadata.formulaCount}</p>
|
|
|
|
<p>Named Ranges: ${analysis.metadata.namedRanges.length}</p>
|
|
|
|
<p>Volatile Functions: ${analysis.metadata.volatileFunctions}</p>
|
|
|
|
<p>External References: ${analysis.metadata.externalReferences}</p>
|
|
|
|
`;
|
|
|
|
|
|
|
|
if (analysis.issues.length === 0) {
|
|
|
|
issuesContent.innerHTML = '<p>No issues found!</p>';
|
|
|
|
} else {
|
|
|
|
issuesContent.innerHTML = analysis.issues.map(issue => `
|
|
|
|
<div class="issue ${issue.severity}">
|
|
|
|
<strong>${issue.type.toUpperCase()}: ${issue.severity}</strong>
|
|
|
|
<p>${issue.message}</p>
|
|
|
|
<p>Location: Sheet "${issue.sheet}", Cell ${issue.cell}</p>
|
|
|
|
${issue.suggestion ? `<p>Suggestion: ${issue.suggestion}</p>` : ''}
|
|
|
|
</div>
|
|
|
|
`).join('');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
2024-12-03 15:17:00 +00:00
|
|
|
</html>
|