PLEDIPO/msme-manager-pro
0
1document.addEventListener('DOMContentLoaded', function() {2 // Initialize app3 initApp();4 5 // Event listeners6 document.getElementById('addMsmeBtn').addEventListener('click', openAddMsmeModal);7 document.getElementById('importBtn').addEventListener('click', openImportModal);8 document.getElementById('exportBtn').addEventListener('click', exportData);9 document.getElementById('filterBtn').addEventListener('click', openFilterModal);10 document.getElementById('refreshBtn').addEventListener('click', refreshData);11 document.getElementById('searchInput').addEventListener('input', searchMsmes);12});13 14// MSME Data Management15let msmeData = [];16let filteredData = [];17let sortConfig = { column: null, direction: 'asc' };18 19async function initApp() {20 // Load initial data (in a real app, this would be from an API)21 await loadData();22 updateStats();23}24 25async function loadData() {26 // Simulate API call27 try {28 // In a real app, replace with actual fetch call29 // const response = await fetch('/api/msmes');30 // msmeData = await response.json();31 32 // Mock data for demo33 msmeData = generateMockData();34 filteredData = [...msmeData];35 renderTable();36 } catch (error) {37 console.error('Error loading data:', error);38 showToast('Failed to load MSME data', 'error');39 }40}41 42function generateMockData() {43 const sectors = ['Agriculture', 'Manufacturing', 'Retail', 'Services', 'Construction'];44 const municipalities = ['Baler', 'San Luis', 'Dipaculao', 'Maria Aurora', 'Dingalan'];45 const categories = ['Micro', 'Small', 'Medium'];46 const structures = ['Sole Proprietorship', 'Partnership', 'Corporation'];47 48 const mockData = [];49 50 for (let i = 0; i < 50; i++) {51 mockData.push({52 id: `MSME-${1000 + i}`,53 businessName: `Business ${i + 1}`,54 owner: `Owner ${i + 1}`,55 dtiRegistration: `DTI-${Math.floor(1000 + Math.random() * 9000)}`,56 sector: sectors[Math.floor(Math.random() * sectors.length)],57 municipality: municipalities[Math.floor(Math.random() * municipalities.length)],58 barangay: `Barangay ${Math.floor(Math.random() * 10) + 1}`,59 province: 'Aurora',60 region: 'Central Luzon',61 category: categories[Math.floor(Math.random() * categories.length)],62 contactNumber: `09${Math.floor(100000000 + Math.random() * 900000000)}`,63 businessStructure: structures[Math.floor(Math.random() * structures.length)],64 status: 'Active',65registrationDate: new Date(Date.now() - Math.floor(Math.random() * 365 * 24 * 60 * 60 * 1000)).toISOString().split('T')[0]66 });67 }68 69 return mockData;70}71 72// Table Rendering and Sorting73function renderTable() {74 const table = document.querySelector('custom-msme-table');75 if (table) {76 table.setData(filteredData);77 }78}79 80function sortTable(column) {81 if (sortConfig.column === column) {82 sortConfig.direction = sortConfig.direction === 'asc' ? 'desc' : 'asc';83 } else {84 sortConfig.column = column;85 sortConfig.direction = 'asc';86 }87 88 filteredData.sort((a, b) => {89 const valueA = a[column] || '';90 const valueB = b[column] || '';91 92 if (valueA < valueB) {93 return sortConfig.direction === 'asc' ? -1 : 1;94 }95 if (valueA > valueB) {96 return sortConfig.direction === 'asc' ? 1 : -1;97 }98 return 0;99 });100 101 renderTable();102}103 104// Search and Filter105function searchMsmes() {106 const query = document.getElementById('searchInput').value.toLowerCase();107 108 if (!query) {109 filteredData = [...msmeData];110 } else {111 filteredData = msmeData.filter(msme => 112 Object.values(msme).some(value => 113 String(value).toLowerCase().includes(query)114 )115 );116 }117 118 renderTable();119 updateStats();120}121 122function applyFilters(filters) {123 filteredData = msmeData.filter(msme => {124 return Object.entries(filters).every(([key, value]) => {125 if (!value) return true;126 return String(msme[key]).toLowerCase() === value.toLowerCase();127 });128 });129 130 renderTable();131 updateStats();132}133 134// Stats Update135function updateStats() {136 document.getElementById('totalMsmes').textContent = filteredData.length;137}138 139// Modal Functions140function openAddMsmeModal() {141 const modalContainer = document.getElementById('modalContainer');142 modalContainer.innerHTML = `143 <div class="modal-overlay">144 <div class="modal-content fade-in">145 <div class="p-6 hovered-element">146 <div class="flex justify-between items-center mb-4">147 <h2 class="text-xl font-bold text-gray-800 hovered-element">Add New MSME</h2>148 <button id="closeModal" class="text-gray-400 hover:text-gray-600">149 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>150 </button>151 </div>152 153 <form id="msmeForm" class="space-y-4">154 <div class="grid grid-cols-1 md:grid-cols-2 gap-4">155 <div class="form-group">156 <label for="businessName" class="form-label hovered-element">Business Name</label>157 <input type="text" id="businessName" class="form-input" required="">158 </div>159 <div class="form-group">160 <label for="owner" class="form-label">Owner</label>161 <input type="text" id="owner" class="form-input" required="">162 </div>163 <div class="form-group">164 <label for="dtiRegistration" class="form-label">DTI Registration No</label>165 <input type="text" id="dtiRegistration" class="form-input">166 </div>167 <div class="form-group">168 <label for="contactNumber" class="form-label">Contact Number</label>169 <input type="tel" id="contactNumber" class="form-input">170 </div>171 <div class="form-group">172 <label for="sector" class="form-label">Sector</label>173 <select id="sector" class="form-select">174 <option value="">Select Sector</option>175 <option value="Agriculture">Agriculture</option>176 <option value="Manufacturing">Manufacturing</option>177 <option value="Retail">Retail</option>178 <option value="Services">Services</option>179 <option value="Construction">Construction</option>180 </select>181 </div>182 <div class="form-group">183 <label for="category" class="form-label">Category</label>184 <select id="category" class="form-select">185 <option value="">Select Category</option>186 <option value="Micro">Micro</option>187 <option value="Small">Small</option>188 <option value="Medium">Medium</option>189 </select>190 </div>191 </div>192 193 <div class="grid grid-cols-1 md:grid-cols-3 gap-4">194 <div class="form-group">195 <label for="municipality" class="form-label">Municipality</label>196 <select id="municipality" class="form-select">197 <option value="">Select Municipality</option>198 <option value="Baler">Baler</option>199 <option value="San Luis">San Luis</option>200 <option value="Dipaculao">Dipaculao</option>201 <option value="Maria Aurora">Maria Aurora</option>202 <option value="Dingalan">Dingalan</option>203 </select>204 </div>205 <div class="form-group">206 <label for="barangay" class="form-label">Barangay</label>207 <input type="text" id="barangay" class="form-input">208 </div>209 <div class="form-group">210 <label for="businessStructure" class="form-label">Business Structure</label>211 <select id="businessStructure" class="form-select">212 <option value="">Select Structure</option>213 <option value="Sole Proprietorship">Sole Proprietorship</option>214 <option value="Partnership">Partnership</option>215 <option value="Corporation">Corporation</option>216 </select>217 </div>218 </div>219 220 <div class="flex justify-end space-x-3 pt-4">221 <button type="button" id="cancelForm" class="btn-secondary">Cancel</button>222 <button type="submit" class="btn-primary">Save MSME</button>223 </div>224 </form>225 </div>226 </div>227 </div>228 `;229 // Replace feather icons230 if (window.feather) {231 feather.replace();232 }233 234 // Close modal handlers235 const closeModal = () => modalContainer.innerHTML = '';236 document.getElementById('closeModal').addEventListener('click', closeModal);237 document.getElementById('cancelForm').addEventListener('click', closeModal);238 239 // Form submission240 document.getElementById('msmeForm').addEventListener('submit', (e) => {241 e.preventDefault();242 saveNewMsme();243 closeModal();244 });245}246function openImportModal() {247 const modalContainer = document.getElementById('modalContainer');248 modalContainer.innerHTML = `249 <div class="modal-overlay">250 <div class="modal-content fade-in" style="width: 500px;">251 <div class="p-6">252 <div class="flex justify-between items-center mb-4">253 <h2 class="text-xl font-bold text-gray-800">Import MSME Data</h2>254 <button id="closeImportModal" class="text-gray-400 hover:text-gray-600">255 <i data-feather="x"></i>256 </button>257 </div>258 259 <div class="space-y-4">260 <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">261 <div class="flex flex-col items-center justify-center">262 <i data-feather="upload-cloud" class="w-12 h-12 text-gray-400 mb-2"></i>263 <p class="text-gray-600">Drag & drop your CSV file here</p>264 <p class="text-gray-400 text-sm mt-1">or</p>265 <label for="fileInput" class="btn-primary inline-block mt-2 cursor-pointer">266 <input type="file" id="fileInput" class="hidden" accept=".csv">267 Select File268 </label>269 </div>270 </div>271 272 <div class="bg-blue-50 p-3 rounded-md text-sm text-blue-700">273 <p><strong>File Requirements:</strong></p>274 <ul class="list-disc pl-5 mt-1">275 <li>CSV format (comma separated values)</li>276 <li>First row should contain headers</li>277 <li>Max file size: 5MB</li>278 </ul>279 </div>280 281 <div class="flex justify-end space-x-3 pt-4">282 <button type="button" id="cancelImport" class="btn-secondary">Cancel</button>283 <button type="button" id="confirmImport" class="btn-primary" disabled>Import Data</button>284 </div>285 </div>286 </div>287 </div>288 </div>289 `;290 291 feather.replace();292 293 const closeModal = () => modalContainer.innerHTML = '';294 document.getElementById('closeImportModal').addEventListener('click', closeModal);295 document.getElementById('cancelImport').addEventListener('click', closeModal);296 297 const fileInput = document.getElementById('fileInput');298 const confirmBtn = document.getElementById('confirmImport');299 300 fileInput.addEventListener('change', (e) => {301 const file = e.target.files[0];302 if (file) {303 if (file.size > 5 * 1024 * 1024) {304 showToast('File size exceeds 5MB limit', 'error');305 return;306 }307 308 if (!file.name.endsWith('.csv')) {309 showToast('Please select a CSV file', 'error');310 return;311 }312 313 confirmBtn.disabled = false;314 showToast(`Selected file: ${file.name}`, 'success');315 }316 });317 318 confirmBtn.addEventListener('click', () => {319 const file = fileInput.files[0];320 if (!file) return;321 322 const reader = new FileReader();323 reader.onload = (e) => {324 try {325 const csvData = e.target.result;326 const importedData = parseCSV(csvData);327 328 if (importedData.length > 0) {329 msmeData = [...importedData, ...msmeData];330 filteredData = [...msmeData];331 renderTable();332 updateStats();333 showToast(`Successfully imported ${importedData.length} MSMEs`, 'success');334 closeModal();335 } else {336 showToast('No valid data found in the file', 'error');337 }338 } catch (error) {339 console.error('Error parsing CSV:', error);340 showToast('Error processing CSV file', 'error');341 }342 };343 reader.readAsText(file);344 });345}346 347function parseCSV(csvString) {348 const lines = csvString.split('\n');349 const headers = lines[0].split(',').map(h => h.trim());350 const result = [];351 352 for (let i = 1; i < lines.length; i++) {353 if (!lines[i].trim()) continue;354 355 const obj = {};356 const currentline = lines[i].split(',');357 358 for (let j = 0; j < headers.length; j++) {359 obj[headers[j]] = currentline[j] ? currentline[j].trim() : '';360 }361 362 // Map CSV fields to our data structure363 const mappedData = {364 id: `MSME-${1000 + msmeData.length + i}`,365 businessName: obj['Business Name'] || obj['businessName'] || `Business ${i}`,366 owner: obj['Owner'] || obj['owner'] || `Owner ${i}`,367 dtiRegistration: obj['DTI Registration'] || obj['dtiRegistration'] || '',368 sector: obj['Sector'] || obj['sector'] || 'Services',369 municipality: obj['Municipality'] || obj['municipality'] || 'Baler',370 barangay: obj['Barangay'] || obj['barangay'] || 'Barangay 1',371 province: obj['Province'] || obj['province'] || 'Aurora',372 region: obj['Region'] || obj['region'] || 'Central Luzon',373 category: obj['Category'] || obj['category'] || 'Micro',374 contactNumber: obj['Contact Number'] || obj['contactNumber'] || '',375 businessStructure: obj['Business Structure'] || obj['businessStructure'] || 'Sole Proprietorship',376 status: 'Active',377 registrationDate: new Date().toISOString().split('T')[0]378 };379 380 result.push(mappedData);381 }382 383 return result;384}385function openFilterModal() {386 const modalContainer = document.getElementById('modalContainer');387 modalContainer.innerHTML = `388 <div class="modal-overlay">389 <div class="modal-content fade-in" style="width: 500px;">390 <div class="p-6">391 <div class="flex justify-between items-center mb-4">392 <h2 class="text-xl font-bold text-gray-800 hovered-element">Advanced Filters</h2>393 <button id="closeFilterModal" class="text-gray-400 hover:text-gray-600 hovered-element">394 <i data-feather="x" class="hovered-element"></i>395 </button>396 </div>397 398 <form id="filterForm" class="space-y-4">399 <div class="form-group">400 <label for="filterSector" class="form-label hovered-element">Sector</label>401 <select id="filterSector" class="form-select hovered-element">402 <option value="">All Sectors</option>403 <option value="Agriculture">Agriculture</option>404 <option value="Manufacturing">Manufacturing</option>405 <option value="Retail">Retail</option>406 <option value="Services">Services</option>407 <option value="Construction">Construction</option>408 </select>409 </div>410 411 <div class="form-group">412 <label for="filterMunicipality" class="form-label hovered-element">Municipality</label>413 <select id="filterMunicipality" class="form-select hovered-element">414 <option value="">All Municipalities</option>415 <option value="Baler">Baler</option>416 <option value="San Luis">San Luis</option>417 <option value="Dipaculao">Dipaculao</option>418 <option value="Maria Aurora">Maria Aurora</option>419 <option value="Dingalan">Dingalan</option>420 </select>421 </div>422 423 <div class="form-group">424 <label for="filterCategory" class="form-label hovered-element">Category</label>425 <select id="filterCategory" class="form-select hovered-element">426 <option value="">All Categories</option>427 <option value="Micro">Micro</option>428 <option value="Small">Small</option>429 <option value="Medium">Medium</option>430 </select>431 </div>432 433 <div class="form-group">434 <label for="filterStructure" class="form-label hovered-element">Business Structure</label>435 <select id="filterStructure" class="form-select hovered-element">436 <option value="">All Structures</option>437 <option value="Sole Proprietorship">Sole Proprietorship</option>438 <option value="Partnership">Partnership</option>439 <option value="Corporation">Corporation</option>440 </select>441 </div>442 443 <div class="flex justify-end space-x-3 pt-4">444 <button type="button" id="resetFilters" class="btn-secondary hovered-element">Reset</button>445 <button type="submit" class="btn-primary hovered-element">Apply Filters</button>446 </div>447 </form>448 </div>449 </div>450 </div>451 `;452feather.replace();453 454 document.getElementById('closeFilterModal').addEventListener('click', () => {455 modalContainer.innerHTML = '';456 });457 458 document.getElementById('resetFilters').addEventListener('click', () => {459 document.getElementById('filterForm').reset();460 });461 462 document.getElementById('filterForm').addEventListener('submit', function(e) {463 e.preventDefault();464 const filters = {465 sector: document.getElementById('filterSector').value,466 municipality: document.getElementById('filterMunicipality').value,467 category: document.getElementById('filterCategory').value,468 businessStructure: document.getElementById('filterStructure').value469 };470 applyFilters(filters);471 modalContainer.innerHTML = '';472 });473}474 475// Data Operations476function saveNewMsme() {477 const form = document.getElementById('msmeForm');478 const newMsme = {479 id: `MSME-${1000 + msmeData.length}`,480 businessName: form.businessName.value,481 owner: form.owner.value,482 dtiRegistration: form.dtiRegistration.value,483 sector: form.sector.value,484 municipality: form.municipality.value,485 barangay: form.barangay.value,486 province: 'Aurora',487 region: 'Central Luzon',488 category: form.category.value,489 contactNumber: form.contactNumber.value,490 businessStructure: form.businessStructure.value,491 status: 'Active',492 registrationDate: new Date().toISOString().split('T')[0]493 };494 495 // In a real app, this would be an API call496 msmeData.unshift(newMsme);497 filteredData.unshift(newMsme);498 499 renderTable();500 updateStats();501 document.getElementById('modalContainer').innerHTML = '';502 showToast('MSME added successfully!', 'success');503}504function exportData() {505 if (filteredData.length === 0) {506 showToast('No data to export', 'warning');507 return;508 }509 510 // Get the headers from the first object511 const headers = Object.keys(filteredData[0]);512 513 // Convert the data to CSV format514 let csvContent = headers.join(',') + '\n';515 516 filteredData.forEach(item => {517 const row = headers.map(header => {518 // Escape quotes and wrap in quotes if contains comma519 let value = item[header] || '';520 if (typeof value === 'string' && value.includes(',')) {521 value = `"${value.replace(/"/g, '""')}"`;522 }523 return value;524 });525 csvContent += row.join(',') + '\n';526 });527 528 // Create download link529 const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });530 const url = URL.createObjectURL(blob);531 const link = document.createElement('a');532 link.setAttribute('href', url);533 link.setAttribute('download', `msme_export_${new Date().toISOString().split('T')[0]}.csv`);534 link.style.visibility = 'hidden';535 536 document.body.appendChild(link);537 link.click();538 document.body.removeChild(link);539 540 showToast(`Exported ${filteredData.length} MSMEs`, 'success');541}542function refreshData() {543 loadData();544 showToast('Data refreshed', 'success');545}546 547// Utility Functions548function showToast(message, type = 'info') {549 const toast = document.createElement('div');550 let bgColor = 'bg-blue-500';551 552 if (type === 'success') bgColor = 'bg-green-500';553 if (type === 'error') bgColor = 'bg-red-500';554 if (type === 'warning') bgColor = 'bg-yellow-500';555 556 toast.className = `fixed bottom-4 right-4 ${bgColor} text-white px-4 py-2 rounded-md shadow-lg flex items-center`;557 toast.innerHTML = `558 <span>${message}</span>559 `;560 561 document.body.appendChild(toast);562 563 setTimeout(() => {564 toast.classList.add('opacity-0', 'transition-opacity', 'duration-300');565 setTimeout(() => toast.remove(), 300);566 }, 3000);567}