CoolFace
Apppublic

GooseMkz/suppliersync-pro-dashboard

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
script.js703 linesDownload Raw Back to root
1 2document.addEventListener('DOMContentLoaded', function() {3    // Handle sidebar collapse4    document.addEventListener('sidebarCollapse', (e) => {5        if (e.detail.collapsed) {6            document.body.classList.add('sidebar-collapsed');7        } else {8            document.body.classList.remove('sidebar-collapsed');9        }10    });11    12// Initialize Feather Icons13    feather.replace();14 15    // Mock data for suppliers16    const suppliers = [17        {18            id: 1,19            companyName: "ООО 'ТехноПром'",20            inn: 1234567890,21            warehouseAddress: "г. Москва, ул. Промышленная, д. 42",22            contactPerson: "Иванов Сергей Петрович",23            phone: "+7 (495) 123-45-67",24            email: "info@technoprom.ru",25            utzManager: "Иванов И.И.",26            productManager: "Смирнова А.В.",27            status: "active",28            date: "2023-05-15"29        },30        {31            id: 2,32            companyName: "АО 'СтройМатериалы'",33            inn: 9876543210,34            warehouseAddress: "г. Санкт-Петербург, пр. Ленина, д. 12",35            contactPerson: "Петрова Мария Ивановна",36            phone: "+7 (812) 987-65-43",37            email: "contact@stroymat.ru",38            utzManager: "Петров П.П.",39            productManager: "Кузнецова Е.П.",40            status: "active",41            date: "2023-06-20"42        },43        {44            id: 3,45            companyName: "ЗАО 'Электрон'",46            inn: 4567890123,47            warehouseAddress: "г. Новосибирск, ул. Электронная, д. 5",48            contactPerson: "Сидоров Алексей Викторович",49            phone: "+7 (383) 456-78-90",50            email: "sales@electron.ru",51            utzManager: "Сидоров С.С.",52            productManager: "Васильева О.М.",53            status: "inactive",54            date: "2023-04-10"55        },56        {57            id: 4,58            companyName: "ООО 'АгроТех'",59            inn: 3456789012,60            warehouseAddress: "г. Казань, ул. Аграрная, д. 15",61            contactPerson: "Галимов Рамиль Фаритович",62            phone: "+7 (843) 345-67-89",63            email: "office@agrotech.ru",64            utzManager: "Иванов И.И.",65            productManager: "Кузнецова Е.П.",66            status: "active",67            date: "2023-07-05"68        },69        {70            id: 5,71            companyName: "ПАО 'МеталлТрейд'",72            inn: 5678901234,73            warehouseAddress: "г. Екатеринбург, ул. Металлургов, д. 24",74            contactPerson: "Кузнецов Дмитрий Сергеевич",75            phone: "+7 (343) 567-89-01",76            email: "metall@metalltrade.ru",77            utzManager: "Петров П.П.",78            productManager: "Смирнова А.В.",79            status: "active",80            date: "2023-08-12"81        },82        {83            id: 6,84            companyName: "ООО 'ТекстильГрупп'",85            inn: 6789012345,86            warehouseAddress: "г. Ростов-на-Дону, ул. Текстильная, д. 8",87            contactPerson: "Волкова Ольга Николаевна",88            phone: "+7 (863) 678-90-12",89            email: "textile@textilegroup.ru",90            utzManager: "Сидоров С.С.",91            productManager: "Васильева О.М.",92            status: "inactive",93            date: "2023-03-18"94        },95        {96            id: 7,97            companyName: "АО 'ХимПром'",98            inn: 7890123456,99            warehouseAddress: "г. Нижний Новгород, ул. Химиков, д. 3",100            contactPerson: "Николаев Андрей Владимирович",101            phone: "+7 (831) 789-01-23",102            email: "info@khimprom.ru",103            utzManager: "Иванов И.И.",104            productManager: "Кузнецова Е.П.",105            status: "active",106            date: "2023-09-22"107        },108        {109            id: 8,110            companyName: "ООО 'СтройЭксперт'",111            inn: 8901234567,112            warehouseAddress: "г. Самара, ул. Строителей, д. 17",113            contactPerson: "Федоров Михаил Александрович",114            phone: "+7 (846) 890-12-34",115            email: "contact@stroiexpert.ru",116            utzManager: "Петров П.П.",117            productManager: "Смирнова А.В.",118            status: "active",119            date: "2023-10-15"120        },121        {122            id: 9,123            companyName: "ЗАО 'АвтоДеталь'",124            inn: 9012345678,125            warehouseAddress: "г. Уфа, ул. Автозаводская, д. 9",126            contactPerson: "Гареев Артур Рамилевич",127            phone: "+7 (347) 901-23-45",128            email: "sales@autodetal.ru",129            utzManager: "Сидоров С.С.",130            productManager: "Васильева О.М.",131            status: "active",132            date: "2023-11-05"133        },134        {135            id: 10,136            companyName: "ООО 'ПищеПром'",137            inn: 1122334455,138            warehouseAddress: "г. Краснодар, ул. Пищевая, д. 21",139            contactPerson: "Семенова Елена Викторовна",140            phone: "+7 (861) 112-23-44",141            email: "office@pisheprom.ru",142            utzManager: "Иванов И.И.",143            productManager: "Кузнецова Е.П.",144            status: "inactive",145            date: "2023-02-28"146        }147    ];148 149    // DOM Elements150    const suppliersTableBody = document.getElementById('suppliersTableBody');151    const addSupplierBtn = document.getElementById('addSupplierBtn');152    const exportBtn = document.getElementById('exportBtn');153    const addSupplierModal = document.getElementById('addSupplierModal');154    const deleteModal = document.getElementById('deleteModal');155    const searchInput = document.getElementById('searchInput');156    const statusFilter = document.getElementById('statusFilter');157    const managerFilter = document.getElementById('managerFilter');158    const supplierForm = document.getElementById('supplierForm');159    const saveSupplierBtn = document.getElementById('saveSupplierBtn');160    const cancelSupplierBtn = document.getElementById('cancelSupplierBtn');161    const confirmDeleteBtn = document.getElementById('confirmDeleteBtn');162    const cancelDeleteBtn = document.getElementById('cancelDeleteBtn');163    const deleteModalText = document.getElementById('deleteModalText');164    const paginationInfo = document.getElementById('paginationInfo');165    const pageNumbers = document.getElementById('pageNumbers');166    const prevPage = document.getElementById('prevPage');167    const nextPage = document.getElementById('nextPage');168    const prevPageMobile = document.getElementById('prevPageMobile');169    const nextPageMobile = document.getElementById('nextPageMobile');170    const toastContainer = document.getElementById('toastContainer');171 172    // Variables173    let currentSuppliers = [...suppliers];174    let currentPage = 1;175    const itemsPerPage = 5;176    let sortColumn = null;177    let sortDirection = 'asc';178    let supplierToDeleteId = null;179    let isEditing = false;180    let currentEditId = null;181 182    // Initialize the table183    function initTable() {184        renderSuppliers();185        setupPagination();186        setupSorting();187    }188 189    // Render suppliers to the table190    function renderSuppliers() {191        suppliersTableBody.innerHTML = '';192        193        const filteredSuppliers = filterSuppliers();194        const paginatedSuppliers = paginateSuppliers(filteredSuppliers);195        196        paginatedSuppliers.forEach(supplier => {197            const row = document.createElement('tr');198            row.className = 'hover:bg-gray-50 transition';199            row.innerHTML = `200                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">${supplier.companyName}</td>201                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.inn}</td>202                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.warehouseAddress}</td>203                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.contactPerson}</td>204                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.phone}</td>205                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.email}</td>206                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.utzManager}</td>207                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${supplier.productManager}</td>208                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">209                    <span class="badge ${supplier.status === 'active' ? 'badge-active' : 'badge-inactive'}">210                        ${supplier.status === 'active' ? 'Активный' : 'Неактивный'}211                    </span>212                </td>213                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${formatDate(supplier.date)}</td>214                <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">215                    <button class="text-blue-600 hover:text-blue-900 mr-3 edit-btn" data-id="${supplier.id}">216                        <i data-feather="edit" class="w-4 h-4"></i>217                    </button>218                    <button class="text-red-600 hover:text-red-900 delete-btn" data-id="${supplier.id}">219                        <i data-feather="trash-2" class="w-4 h-4"></i>220                    </button>221                </td>222            `;223            suppliersTableBody.appendChild(row);224        });225        226        // Update pagination info227        const filteredCount = filteredSuppliers.length;228        const startItem = (currentPage - 1) * itemsPerPage + 1;229        const endItem = Math.min(currentPage * itemsPerPage, filteredCount);230        231        paginationInfo.innerHTML = `232            Показано <span class="font-medium">${startItem}</span> - <span class="font-medium">${endItem}</span> из <span class="font-medium">${filteredCount}</span> поставщиков233        `;234        235        // Refresh feather icons236        feather.replace();237        238        // Add event listeners to edit and delete buttons239        document.querySelectorAll('.edit-btn').forEach(btn => {240            btn.addEventListener('click', (e) => {241                const id = parseInt(e.currentTarget.getAttribute('data-id'));242                editSupplier(id);243            });244        });245        246        document.querySelectorAll('.delete-btn').forEach(btn => {247            btn.addEventListener('click', (e) => {248                const id = parseInt(e.currentTarget.getAttribute('data-id'));249                confirmDelete(id);250            });251        });252    }253 254    // Filter suppliers based on search and filters255    function filterSuppliers() {256        let filtered = [...suppliers];257        258        // Search filter259        const searchTerm = searchInput.value.toLowerCase();260        if (searchTerm) {261            filtered = filtered.filter(supplier => 262                supplier.companyName.toLowerCase().includes(searchTerm) ||263                supplier.inn.toString().includes(searchTerm) ||264                supplier.email.toLowerCase().includes(searchTerm)265            );266        }267        268        // Status filter269        const statusFilterValue = statusFilter.value;270        if (statusFilterValue) {271            filtered = filtered.filter(supplier => supplier.status === statusFilterValue);272        }273        274        // Manager filter275        const managerFilterValue = managerFilter.value;276        if (managerFilterValue) {277            filtered = filtered.filter(supplier => 278                supplier.utzManager === managerFilterValue || 279                supplier.productManager === managerFilterValue280            );281        }282        283        // Sorting284        if (sortColumn) {285            filtered.sort((a, b) => {286                let valueA, valueB;287                288                if (sortColumn === 'name') {289                    valueA = a.companyName;290                    valueB = b.companyName;291                } else if (sortColumn === 'inn') {292                    valueA = a.inn;293                    valueB = b.inn;294                } else if (sortColumn === 'date') {295                    valueA = new Date(a.date);296                    valueB = new Date(b.date);297                }298                299                if (valueA < valueB) {300                    return sortDirection === 'asc' ? -1 : 1;301                }302                if (valueA > valueB) {303                    return sortDirection === 'asc' ? 1 : -1;304                }305                return 0;306            });307        }308        309        return filtered;310    }311 312    // Paginate suppliers313    function paginateSuppliers(suppliersList) {314        const startIndex = (currentPage - 1) * itemsPerPage;315        return suppliersList.slice(startIndex, startIndex + itemsPerPage);316    }317 318    // Setup pagination319    function setupPagination() {320        const filteredSuppliers = filterSuppliers();321        const totalPages = Math.ceil(filteredSuppliers.length / itemsPerPage);322        323        pageNumbers.innerHTML = '';324        325        for (let i = 1; i <= totalPages; i++) {326            const pageItem = document.createElement('span');327            pageItem.innerHTML = `328                <button class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium ${currentPage === i ? 'text-white bg-blue-600 border-blue-600' : 'text-gray-700 hover:bg-gray-50'} page-btn" data-page="${i}">329                    ${i}330                </button>331            `;332            pageNumbers.appendChild(pageItem);333        }334        335        // Add event listeners to page buttons336        document.querySelectorAll('.page-btn').forEach(btn => {337            btn.addEventListener('click', (e) => {338                const page = parseInt(e.currentTarget.getAttribute('data-page'));339                currentPage = page;340                renderSuppliers();341            });342        });343    }344 345    // Setup sorting346    function setupSorting() {347        document.querySelectorAll('[data-sort]').forEach(header => {348            header.addEventListener('click', (e) => {349                const column = e.currentTarget.getAttribute('data-sort');350                351                // Reset all sort icons352                document.querySelectorAll('.sort-icon').forEach(icon => {353                    icon.classList.add('hidden');354                });355                356                if (sortColumn === column) {357                    // Toggle direction if same column358                    sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';359                } else {360                    // New column, default to ascending361                    sortColumn = column;362                    sortDirection = 'asc';363                }364                365                // Show the correct sort icon366                const icons = e.currentTarget.querySelectorAll('.sort-icon');367                if (sortDirection === 'asc') {368                    icons[0].classList.remove('hidden');369                } else {370                    icons[1].classList.remove('hidden');371                }372                373                // Reset to first page and render374                currentPage = 1;375                renderSuppliers();376            });377        });378    }379 380    // Format date for display381    function formatDate(dateString) {382        const options = { year: 'numeric', month: 'short', day: 'numeric' };383        return new Date(dateString).toLocaleDateString('ru-RU', options);384    }385 386    // Show add supplier modal387    function showAddSupplierModal() {388        isEditing = false;389        document.getElementById('modalTitle').textContent = 'Добавить поставщика';390        resetSupplierForm();391        addSupplierModal.classList.remove('hidden');392        393        // Set focus to first input394        setTimeout(() => {395            document.getElementById('companyName').focus();396        }, 100);397    }398 399    // Show edit supplier modal400    function editSupplier(id) {401        const supplier = suppliers.find(s => s.id === id);402        if (!supplier) return;403        404        isEditing = true;405        currentEditId = id;406        document.getElementById('modalTitle').textContent = 'Редактировать поставщика';407        408        // Fill the form409        document.getElementById('companyName').value = supplier.companyName;410        document.getElementById('inn').value = supplier.inn;411        document.getElementById('warehouseAddress').value = supplier.warehouseAddress;412        document.getElementById('contactPerson').value = supplier.contactPerson;413        document.getElementById('phone').value = supplier.phone;414        document.getElementById('email').value = supplier.email;415        document.getElementById('utzManager').value = supplier.utzManager;416        document.getElementById('productManager').value = supplier.productManager;417        document.getElementById('status').value = supplier.status;418        419        addSupplierModal.classList.remove('hidden');420        421        // Set focus to first input422        setTimeout(() => {423            document.getElementById('companyName').focus();424        }, 100);425    }426 427    // Reset supplier form428    function resetSupplierForm() {429        supplierForm.reset();430        // Clear any validation errors431        document.querySelectorAll('.error-message').forEach(el => el.remove());432        document.querySelectorAll('.input-error').forEach(el => {433            el.classList.remove('input-error');434        });435    }436 437    // Save supplier (add or edit)438    function saveSupplier() {439        // Validate form440        const companyName = document.getElementById('companyName').value.trim();441        const inn = document.getElementById('inn').value.trim();442        const warehouseAddress = document.getElementById('warehouseAddress').value.trim();443        444        let isValid = true;445        446        // Clear previous errors447        document.querySelectorAll('.error-message').forEach(el => el.remove());448        document.querySelectorAll('.input-error').forEach(el => {449            el.classList.remove('input-error');450        });451        452        // Validate required fields453        if (!companyName) {454            showFieldError('companyName', 'Название компании обязательно');455            isValid = false;456        }457        458        if (!inn) {459            showFieldError('inn', 'ИНН обязателен');460            isValid = false;461        } else if (!/^\d{10}$/.test(inn)) {462            showFieldError('inn', 'ИНН должен состоять из 10 цифр');463            isValid = false;464        }465        466        if (!warehouseAddress) {467            showFieldError('warehouseAddress', 'Адрес склада обязателен');468            isValid = false;469        }470        471        if (!isValid) return;472        473        // Create supplier object474        const supplier = {475            companyName,476            inn: parseInt(inn),477            warehouseAddress,478            contactPerson: document.getElementById('contactPerson').value.trim(),479            phone: document.getElementById('phone').value.trim(),480            email: document.getElementById('email').value.trim(),481            utzManager: document.getElementById('utzManager').value,482            productManager: document.getElementById('productManager').value,483            status: document.getElementById('status').value,484            date: new Date().toISOString().split('T')[0] // Today's date485        };486        487        if (isEditing) {488            // Update existing supplier489            const index = suppliers.findIndex(s => s.id === currentEditId);490            if (index !== -1) {491                // Keep the original ID and date492                supplier.id = currentEditId;493                supplier.date = suppliers[index].date;494                suppliers[index] = supplier;495                showToast('Изменения сохранены', 'success');496            }497        } else {498            // Add new supplier499            const newId = suppliers.length > 0 ? Math.max(...suppliers.map(s => s.id)) + 1 : 1;500            supplier.id = newId;501            suppliers.push(supplier);502            showToast('Поставщик успешно добавлен', 'success');503        }504        505        // Close modal and refresh table506        addSupplierModal.classList.add('hidden');507        currentPage = 1; // Reset to first page508        renderSuppliers();509    }510 511    // Show field error512    function showFieldError(fieldId, message) {513        const field = document.getElementById(fieldId);514        field.classList.add('input-error');515        516        const errorElement = document.createElement('p');517        errorElement.className = 'error-message';518        errorElement.textContent = message;519        520        field.parentNode.appendChild(errorElement);521    }522 523    // Confirm delete supplier524    function confirmDelete(id) {525        const supplier = suppliers.find(s => s.id === id);526        if (!supplier) return;527        528        supplierToDeleteId = id;529        deleteModalText.textContent = `Вы уверены, что хотите удалить поставщика "${supplier.companyName}"? Это действие нельзя отменить.`;530        deleteModal.classList.remove('hidden');531    }532 533    // Delete supplier534    function deleteSupplier() {535        const index = suppliers.findIndex(s => s.id === supplierToDeleteId);536        if (index !== -1) {537            const supplierName = suppliers[index].companyName;538            suppliers.splice(index, 1);539            showToast(`Поставщик "${supplierName}" удален`, 'error');540            541            // Reset to first page if the last item on the current page was deleted542            const filteredSuppliers = filterSuppliers();543            const totalPages = Math.ceil(filteredSuppliers.length / itemsPerPage);544            if (currentPage > totalPages) {545                currentPage = Math.max(1, totalPages);546            }547            548            renderSuppliers();549        }550        551        deleteModal.classList.add('hidden');552        supplierToDeleteId = null;553    }554 555    // Show toast notification556    function showToast(message, type) {557        const toast = document.createElement('div');558        toast.className = `toast toast-${type}`;559        560        const icon = type === 'success' ? 'check-circle' : 'alert-circle';561        562        toast.innerHTML = `563            <i data-feather="${icon}" class="toast-icon"></i>564            <span>${message}</span>565            <button class="toast-close">566                <i data-feather="x"></i>567            </button>568        `;569        570        toastContainer.appendChild(toast);571        feather.replace();572        573        // Auto remove after 3 seconds574        setTimeout(() => {575            toast.remove();576        }, 3000);577        578        // Close button579        toast.querySelector('.toast-close').addEventListener('click', () => {580            toast.remove();581        });582    }583 584    // Export to Excel585    function exportToExcel() {586        const filteredSuppliers = filterSuppliers();587        588        // Prepare data for export589        const data = filteredSuppliers.map(supplier => ({590            'Название компании': supplier.companyName,591            'ИНН': supplier.inn,592            'Адрес склада': supplier.warehouseAddress,593            'Контактное лицо': supplier.contactPerson,594            'Телефон': supplier.phone,595            'Email': supplier.email,596            'Менеджер УТЗ': supplier.utzManager,597            'Товарный менеджер': supplier.productManager,598            'Статус': supplier.status === 'active' ? 'Активный' : 'Неактивный',599            'Дата добавления': formatDate(supplier.date)600        }));601        602        // Create worksheet603        const ws = XLSX.utils.json_to_sheet(data);604        605        // Create workbook606        const wb = XLSX.utils.book_new();607        XLSX.utils.book_append_sheet(wb, ws, "Поставщики");608        609        // Generate filename with current date610        const today = new Date();611        const dateStr = `${today.getFullYear()}-${(today.getMonth() + 1).toString().padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}`;612        const filename = `Поставщики_${dateStr}.xlsx`;613        614        // Export to Excel615        XLSX.writeFile(wb, filename);616        617        showToast('Экспорт в Excel выполнен', 'success');618    }619 620    // Event Listeners621    addSupplierBtn.addEventListener('click', showAddSupplierModal);622    exportBtn.addEventListener('click', exportToExcel);623    saveSupplierBtn.addEventListener('click', saveSupplier);624    cancelSupplierBtn.addEventListener('click', () => {625        addSupplierModal.classList.add('hidden');626    });627    confirmDeleteBtn.addEventListener('click', deleteSupplier);628    cancelDeleteBtn.addEventListener('click', () => {629        deleteModal.classList.add('hidden');630        supplierToDeleteId = null;631    });632    633    // Filters and search634    [searchInput, statusFilter, managerFilter].forEach(element => {635        element.addEventListener('change', () => {636            currentPage = 1;637            renderSuppliers();638        });639    });640    641    // Pagination navigation642    prevPage.addEventListener('click', () => {643        if (currentPage > 1) {644            currentPage--;645            renderSuppliers();646        }647    });648    649    nextPage.addEventListener('click', () => {650        const filteredSuppliers = filterSuppliers();651        const totalPages = Math.ceil(filteredSuppliers.length / itemsPerPage);652        653        if (currentPage < totalPages) {654            currentPage++;655            renderSuppliers();656        }657    });658    659    prevPageMobile.addEventListener('click', () => {660        if (currentPage > 1) {661            currentPage--;662            renderSuppliers();663        }664    });665    666    nextPageMobile.addEventListener('click', () => {667        const filteredSuppliers = filterSuppliers();668        const totalPages = Math.ceil(filteredSuppliers.length / itemsPerPage);669        670        if (currentPage < totalPages) {671            currentPage++;672            renderSuppliers();673        }674    });675    676    // Close modals when clicking outside677    [addSupplierModal, deleteModal].forEach(modal => {678        modal.addEventListener('click', (e) => {679            if (e.target === modal) {680                modal.classList.add('hidden');681                if (modal === deleteModal) {682                    supplierToDeleteId = null;683                }684            }685        });686    });687    688    // Close modals with Escape key689    document.addEventListener('keydown', (e) => {690        if (e.key === 'Escape') {691            if (!addSupplierModal.classList.contains('hidden')) {692                addSupplierModal.classList.add('hidden');693            }694            if (!deleteModal.classList.contains('hidden')) {695                deleteModal.classList.add('hidden');696                supplierToDeleteId = null;697            }698        }699    });700    701    // Initialize the table702    initTable();703});