Code for the post at https://jonathanaddington.com/how-small-is-a-small-business/
100% generated by OpenAI’s gpt-4o model on 2024-05-29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Autocomplete Example</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nouislider/distribute/nouislider.min.css">
<style>
body {
font-family: Arial, sans-serif;
}
.autocomplete-suggestions {
border: 1px solid #ccc;
max-height: 150px;
overflow-y: auto;
min-width: 300px; /* Adjust this value as needed */
}
.autocomplete-suggestion {
padding: 8px;
cursor: pointer;
}
.autocomplete-suggestion:hover {
background-color: #e9e9e9;
}
.info {
margin-top: 20px;
font-family: Arial, sans-serif;
}
.info p {
margin: 5px 0;
}
.info h2 {
margin: 10px 0 5px;
font-size: 1.2em;
}
.chart-container {
width: 50%;
margin: auto;
height: 400px; /* Set a fixed height for the chart */
}
#autocomplete {
width: 100%;
padding: 10px;
box-sizing: border-box;
margin-bottom: 20px;
}
@media (max-width: 768px) {
.chart-container {
width: 100%;
height: 400px; /* Maintain the height for smaller screens */
}
}
.controls {
margin-top: 20px;
}
.controls button {
padding: 10px;
margin-right: 10px;
}
.controls .slider-container {
margin-top: 20px;
max-width: 50%;
}
.industry-list {
margin-top: 20px;
}
.industry-list ul {
list-style-type: none;
padding: 0;
}
.industry-list li {
padding: 5px;
cursor: pointer;
}
.industry-list li:hover {
background-color: #e9e9e9;
}
#slider {
margin: 20px 0;
}
.variance-positive {
color: green;
}
.variance-negative {
color: red;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/nouislider/distribute/nouislider.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h1>Industry Information</h1>
<label for="autocomplete">Search by Industry or NAICS Code:</label>
<input type="text" id="autocomplete" placeholder="Type to search...">
<div id="autocomplete-list" class="autocomplete-suggestions"></div>
<div class="info">
<p id="hierarchy"></p>
<p><strong>Limit:</strong> <span id="limit"></span></p>
<p><strong>Average Limit for Sector:</strong> <span id="averageSectorLimit"></span></p>
<p><strong>Variance from Sector Average:</strong> <span id="sectorVariance"></span></p>
<p><strong>Average Limit for Subsector:</strong> <span id="averageSubsectorLimit"></span></p>
<p><strong>Variance from Subsector Average:</strong> <span id="subsectorVariance"></span></p>
</div>
<div class="chart-container">
<canvas id="limitChart"></canvas>
</div>
<div class="controls">
<button id="randomIndustryBtn">Show Random Industry</button>
<button id="smallestIndustryBtn">List Industries with Smallest Limit</button>
<button id="largestIndustryBtn">List Industries with Largest Limit</button>
<div class="slider-container">
<label for="metricSelect">Metric:</label>
<select id="metricSelect">
<option value="revenueLimit">Revenue</option>
<option value="employeeCountLimit">Employees</option>
</select>
<div id="slider"></div>
<span id="sliderValues"></span>
</div>
</div>
<div class="industry-list" id="industryList">
<ul></ul>
</div>
<script>
let data = [];
let chart;
// Fetch data from the API
fetch('https://api.sba.gov/naics/naics.json')
.then(response => response.json())
.then(json => {
data = json;
displayRandomEntry();
initializeSlider();
})
.catch(error => console.error('Error fetching data:', error));
const input = document.getElementById('autocomplete');
const autocompleteList = document.getElementById('autocomplete-list');
const hierarchySpan = document.getElementById('hierarchy');
const limitSpan = document.getElementById('limit');
const averageSectorLimitSpan = document.getElementById('averageSectorLimit');
const sectorVarianceSpan = document.getElementById('sectorVariance');
const averageSubsectorLimitSpan = document.getElementById('averageSubsectorLimit');
const subsectorVarianceSpan = document.getElementById('subsectorVariance');
const limitChartCtx = document.getElementById('limitChart').getContext('2d');
const randomIndustryBtn = document.getElementById('randomIndustryBtn');
const smallestIndustryBtn = document.getElementById('smallestIndustryBtn');
const largestIndustryBtn = document.getElementById('largestIndustryBtn');
const metricSelect = document.getElementById('metricSelect');
const slider = document.getElementById('slider');
const sliderValues = document.getElementById('sliderValues');
const industryList = document.getElementById('industryList').querySelector('ul');
input.addEventListener('input', function() {
const query = this.value.toLowerCase();
autocompleteList.innerHTML = '';
if (query.length === 0) {
return;
}
const filteredData = data.filter(item =>
item.description.toLowerCase().includes(query) || item.id.includes(query)
);
filteredData.forEach(item => {
const div = document.createElement('div');
div.classList.add('autocomplete-suggestion');
div.textContent = `${item.description} (${item.id})`;
div.addEventListener('click', function() {
input.value = `${item.description} (${item.id})`;
displayEntry(item);
autocompleteList.innerHTML = '';
});
autocompleteList.appendChild(div);
});
});
document.addEventListener('click', function(e) {
if (e.target !== input) {
autocompleteList.innerHTML = '';
}
});
randomIndustryBtn.addEventListener('click', displayRandomEntry);
smallestIndustryBtn.addEventListener('click', () => listIndustries('smallest'));
largestIndustryBtn.addEventListener('click', () => listIndustries('largest'));
metricSelect.addEventListener('change', updateSliderRange);
function displayEntry(item) {
hierarchySpan.textContent = `${item.sectorDescription} > ${item.subsectorDescription} > ${item.description} (${item.id})`;
limitSpan.textContent = item.revenueLimit ? `$${item.revenueLimit} million` : (item.employeeCountLimit ? `${item.employeeCountLimit} employees` : 'N/A');
const sectorItems = data.filter(d => d.sectorId === item.sectorId);
const subsectorItems = data.filter(d => d.subsectorId === item.subsectorId);
let sectorLimits = [];
let subsectorLimits = [];
let isRevenue = false;
if (item.revenueLimit !== null) {
sectorLimits = sectorItems.map(d => d.revenueLimit).filter(limit => limit !== null);
subsectorLimits = subsectorItems.map(d => d.revenueLimit).filter(limit => limit !== null);
isRevenue = true;
} else if (item.employeeCountLimit !== null) {
sectorLimits = sectorItems.map(d => d.employeeCountLimit).filter(limit => limit !== null);
subsectorLimits = subsectorItems.map(d => d.employeeCountLimit).filter(limit => limit !== null);
}
const averageSectorLimit = Math.round(sectorLimits.reduce((sum, limit) => sum + limit, 0) / sectorLimits.length);
const sectorVariance = Math.round((item.revenueLimit || item.employeeCountLimit) - averageSectorLimit);
const averageSubsectorLimit = Math.round(subsectorLimits.reduce((sum, limit) => sum + limit, 0) / subsectorLimits.length);
const subsectorVariance = Math.round((item.revenueLimit || item.employeeCountLimit) - averageSubsectorLimit);
averageSectorLimitSpan.textContent = isRevenue ? `$${averageSectorLimit} million` : `${averageSectorLimit} employees`;
sectorVarianceSpan.textContent = isRevenue ? `${sectorVariance > 0 ? '+' : ''}$${sectorVariance} million` : `${sectorVariance > 0 ? '+' : ''}${sectorVariance} employees`;
averageSubsectorLimitSpan.textContent = isRevenue ? `$${averageSubsectorLimit} million` : `${averageSubsectorLimit} employees`;
subsectorVarianceSpan.textContent = isRevenue ? `${subsectorVariance > 0 ? '+' : ''}$${subsectorVariance} million` : `${subsectorVariance > 0 ? '+' : ''}${subsectorVariance} employees`;
// Add classes for variance colors
sectorVarianceSpan.className = sectorVariance >= 0 ? 'variance-positive' : 'variance-negative';
subsectorVarianceSpan.className = subsectorVariance >= 0 ? 'variance-positive' : 'variance-negative';
// Update the chart
updateChart(item, averageSectorLimit, averageSubsectorLimit, isRevenue);
}
function displayRandomEntry() {
if (data.length > 0) {
const randomIndex = Math.floor(Math.random() * data.length);
const randomItem = data[randomIndex];
displayEntry(randomItem);
}
}
function updateChart(item, averageSectorLimit, averageSubsectorLimit, isRevenue) {
const limit = item.revenueLimit || item.employeeCountLimit;
const labels = [item.description, item.sectorDescription, item.subsectorDescription];
const data = [limit, averageSectorLimit, averageSubsectorLimit];
const label = isRevenue ? 'Revenue (in million $)' : 'Employees';
if (chart) {
chart.destroy();
}
chart = new Chart(limitChartCtx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: label,
data: data,
backgroundColor: ['rgba(75, 192, 192, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)'],
borderColor: ['rgba(75, 192, 192, 1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)'],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true
}
}
}
});
}
function listIndustries(type) {
industryList.innerHTML = '';
let sortedData;
if (type === 'smallest') {
sortedData = [...data].sort((a, b) => (a.revenueLimit || a.employeeCountLimit || Number.MAX_VALUE) - (b.revenueLimit || b.employeeCountLimit || Number.MAX_VALUE));
} else if (type === 'largest') {
sortedData = [...data].sort((a, b) => (b.revenueLimit || b.employeeCountLimit || Number.MIN_VALUE) - (a.revenueLimit || a.employeeCountLimit || Number.MIN_VALUE));
}
sortedData.forEach(item => {
const li = document.createElement('li');
li.textContent = `${item.description} (${item.id})`;
li.addEventListener('click', () => displayEntry(item));
industryList.appendChild(li);
});
}
function updateRange() {
const metric = metricSelect.value;
const [lower, upper] = slider.noUiSlider.get().map(Number);
sliderValues.textContent = `Selected range: ${lower} - ${upper}`;
industryList.innerHTML = '';
const filteredData = data.filter(item => {
const limit = item[metric];
return limit !== null && limit >= lower && limit <= upper;
});
const sortedData = filteredData.sort((a, b) => a.id - b.id);
sortedData.forEach(item => {
const li = document.createElement('li');
li.textContent = `${item.description} (${item.id})`;
li.addEventListener('click', () => displayEntry(item));
industryList.appendChild(li);
});
}
function initializeSlider() {
// Initialize the slider with a default range
noUiSlider.create(slider, {
start: [0, 100],
connect: true,
range: {
'min': 0,
'max': 100
},
tooltips: [true, true]
});
slider.noUiSlider.on('update', updateRange);
// Set the initial range based on the selected metric
updateSliderRange();
}
function updateSliderRange() {
const metric = metricSelect.value;
const minLimit = Math.min(...data.map(item => item[metric] || Infinity));
const maxLimit = Math.max(...data.map(item => item[metric] || 0));
slider.noUiSlider.updateOptions({
range: {
'min': minLimit,
'max': maxLimit
},
start: [minLimit, maxLimit]
});
// Update the range display
updateRange();
}
</script>
</body>
</html>
0 Comments