{% extends "base.html" %} {% block title %}Zarządzanie Firmami - Norda Biznes Partner{% endblock %} {% block extra_css %} {% endblock %} {% block content %}

Zarządzanie Firmami

Przeglądaj i zarządzaj firmami w katalogu

Eksport CSV Kreator
{{ total_companies }}
Wszystkich
{{ active_count }}
Aktywnych
{{ pending_count }}
Oczekujących
{{ inactive_count }}
Nieaktywnych

Firmy ({{ companies|length }})

{% if companies %} {% for company in companies %} {% endfor %}
ID Firma NIP Kategoria Status Użyt. Akcje
{{ company.id }}
{{ company.name }} {% set age_days = ((now - company.created_at).total_seconds() / 86400)|int if company.created_at and now else 999 %} {% if age_days <= 7 %} NOWA {% endif %} {% if company.admin_notes %} {% endif %}
{% if company.address_city %} {{ company.address_city }} {% endif %}
{{ company.nip or '-' }} {{ company.category.name if company.category else '-' }} {{ company.status or 'pending' }} {{ company.users|length if company.users else 0 }}
{% if company.status == 'archived' and current_user.can_manage_users() %} {% elif company.status != 'archived' %} {% endif %}
{% else %}

Brak firm spełniających kryteria

{% endif %}
{% endblock %} {% block extra_js %} const csrfToken = '{{ csrf_token() }}'; let editCompanyId = null; let confirmCallback = null; function showToast(message, type = 'success') { const container = document.getElementById('toastContainer'); const toast = document.createElement('div'); toast.className = `toast ${type}`; const iconSvg = { success: '', error: '' }; toast.innerHTML = ` ${iconSvg[type] || iconSvg.success} ${message} `; container.appendChild(toast); setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease forwards'; setTimeout(() => toast.remove(), 300); }, 5000); } // Add Modal function openAddModal() { document.getElementById('addName').value = ''; document.getElementById('addNip').value = ''; document.getElementById('addCategory').value = ''; document.getElementById('addStatus').value = 'pending'; document.getElementById('addEmail').value = ''; document.getElementById('addPhone').value = ''; document.getElementById('addCity').value = ''; document.getElementById('addModal').classList.add('active'); } function closeAddModal() { document.getElementById('addModal').classList.remove('active'); } async function confirmAdd() { const name = document.getElementById('addName').value.trim(); if (!name) { showToast('Nazwa firmy jest wymagana', 'error'); return; } try { const response = await fetch('/admin/companies/add', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ name: name, nip: document.getElementById('addNip').value.trim() || null, category_id: document.getElementById('addCategory').value || null, status: document.getElementById('addStatus').value, email: document.getElementById('addEmail').value.trim() || null, phone: document.getElementById('addPhone').value.trim() || null, address_city: document.getElementById('addCity').value.trim() || null }) }); const data = await response.json(); if (data.success) { closeAddModal(); showToast(data.message, 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // Edit Modal async function openEditModal(companyId) { editCompanyId = companyId; try { const response = await fetch(`/admin/companies/${companyId}`); const data = await response.json(); if (data.success) { const c = data.company; document.getElementById('editName').value = c.name || ''; document.getElementById('editNip').value = c.nip || ''; document.getElementById('editCategory').value = c.category_id || ''; document.getElementById('editStatus').value = c.status || 'pending'; document.getElementById('editEmail').value = c.email || ''; document.getElementById('editPhone').value = c.phone || ''; document.getElementById('editCity').value = c.address_city || ''; document.getElementById('editStreet').value = c.address_street || ''; document.getElementById('editPostal').value = c.address_postal || ''; document.getElementById('editAdminNotes').value = c.admin_notes || ''; document.getElementById('editModal').classList.add('active'); } else { showToast(data.error || 'Nie można pobrać danych', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } function closeEditModal() { editCompanyId = null; document.getElementById('editModal').classList.remove('active'); } async function saveEdit() { if (!editCompanyId) return; const name = document.getElementById('editName').value.trim(); if (!name) { showToast('Nazwa firmy jest wymagana', 'error'); return; } try { const response = await fetch(`/admin/companies/${editCompanyId}/update`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ name: name, nip: document.getElementById('editNip').value.trim() || null, category_id: document.getElementById('editCategory').value || null, status: document.getElementById('editStatus').value, email: document.getElementById('editEmail').value.trim() || null, phone: document.getElementById('editPhone').value.trim() || null, address_city: document.getElementById('editCity').value.trim() || null, address_street: document.getElementById('editStreet').value.trim() || null, address_postal: document.getElementById('editPostal').value.trim() || null, admin_notes: document.getElementById('editAdminNotes').value.trim() || null }) }); const data = await response.json(); if (data.success) { closeEditModal(); showToast(data.message, 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // Toggle Status async function toggleStatus(companyId) { try { const response = await fetch(`/admin/companies/${companyId}/toggle-status`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // People Modal async function openPeopleModal(companyId, companyName) { document.getElementById('peopleModalTitle').textContent = `Osoby powiązane - ${companyName}`; document.getElementById('peopleList').innerHTML = '
Ładowanie...
'; document.getElementById('peopleModal').classList.add('active'); try { const response = await fetch(`/admin/companies/${companyId}/people`); const data = await response.json(); if (data.success) { if (data.people.length === 0) { document.getElementById('peopleList').innerHTML = '
Brak powiązanych osób
'; } else { let html = ''; data.people.forEach(p => { html += `
${p.imiona} ${p.nazwisko}
${p.role} (${p.role_category})${p.shares_percent ? ' - ' + p.shares_percent + '%' : ''}
`; }); document.getElementById('peopleList').innerHTML = html; } } else { document.getElementById('peopleList').innerHTML = '
Błąd pobierania danych
'; } } catch (error) { document.getElementById('peopleList').innerHTML = '
Błąd połączenia
'; } } function closePeopleModal() { document.getElementById('peopleModal').classList.remove('active'); } // Users Modal let usersModalCompanyId = null; async function openUsersModal(companyId, companyName) { usersModalCompanyId = companyId; document.getElementById('usersModalTitle').textContent = `Użytkownicy - ${companyName}`; document.getElementById('usersList').innerHTML = '
Ładowanie...
'; document.getElementById('usersModal').classList.add('active'); await loadCompanyUsers(companyId); await loadAvailableUsers(); } function closeUsersModal() { usersModalCompanyId = null; document.getElementById('usersModal').classList.remove('active'); } async function loadCompanyUsers(companyId) { try { const response = await fetch(`/admin/companies/${companyId}/users`); const data = await response.json(); if (data.success) { if (data.users.length === 0) { document.getElementById('usersList').innerHTML = '
Brak przypisanych użytkowników
'; } else { let html = ''; data.users.forEach(u => { html += `
${u.name || u.email}
${u.email} · ${u.role || 'user'}
`; }); document.getElementById('usersList').innerHTML = html; } } else { document.getElementById('usersList').innerHTML = '
Błąd pobierania danych
'; } } catch (error) { document.getElementById('usersList').innerHTML = '
Błąd połączenia
'; } } async function loadAvailableUsers() { try { const response = await fetch('/admin/users/list-all'); const data = await response.json(); const select = document.getElementById('assignUserSelect'); select.innerHTML = ''; if (data.success) { data.users.filter(u => !u.company_id).forEach(u => { const option = document.createElement('option'); option.value = u.id; option.textContent = `${u.name || u.email} (${u.email})`; select.appendChild(option); }); } } catch (error) { console.error('Error loading users:', error); } } async function assignUserToCompany() { if (!usersModalCompanyId) return; const userId = document.getElementById('assignUserSelect').value; if (!userId) { showToast('Wybierz użytkownika', 'error'); return; } try { const response = await fetch(`/admin/companies/${usersModalCompanyId}/assign-user`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ user_id: parseInt(userId) }) }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); await loadCompanyUsers(usersModalCompanyId); await loadAvailableUsers(); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } async function unassignUser(companyId, userId, userEmail) { try { const response = await fetch(`/admin/companies/${companyId}/unassign-user`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ user_id: userId }) }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); await loadCompanyUsers(companyId); await loadAvailableUsers(); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // Delete (Archive) Company function deleteCompany(companyId, companyName) { document.getElementById('confirmIcon').className = 'modal-icon warning'; document.getElementById('confirmTitle').textContent = 'Archiwizuj firmę'; document.getElementById('confirmDescription').textContent = `Czy na pewno chcesz zarchiwizować firmę "${companyName}"? Firma nie będzie widoczna w katalogu.`; document.getElementById('confirmAction').textContent = 'Potwierdź'; document.getElementById('confirmAction').className = 'btn btn-danger'; confirmCallback = async () => { try { const response = await fetch(`/admin/companies/${companyId}/delete`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } }; document.getElementById('confirmModal').classList.add('active'); } // Hard Delete (Permanent) - only for archived companies function hardDeleteCompany(companyId, companyName) { document.getElementById('confirmIcon').className = 'modal-icon danger'; document.getElementById('confirmTitle').textContent = 'Trwałe usunięcie firmy'; document.getElementById('confirmDescription').innerHTML = `Czy na pewno chcesz trwale usunąć firmę "${companyName}"?

` + 'Ta operacja jest NIEODWRACALNA!
' + 'Wszystkie dane firmy zostaną permanentnie usunięte.'; document.getElementById('confirmAction').textContent = 'Trwale usuń'; document.getElementById('confirmAction').className = 'btn btn-danger'; confirmCallback = async () => { try { const response = await fetch(`/admin/companies/${companyId}/hard-delete`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { const row = document.querySelector(`tr[data-company-id="${companyId}"]`); if (row) row.remove(); showToast(data.message, 'success'); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } }; document.getElementById('confirmModal').classList.add('active'); } function closeConfirmModal() { document.getElementById('confirmModal').classList.remove('active'); confirmCallback = null; } document.getElementById('confirmAction').addEventListener('click', function() { if (confirmCallback) confirmCallback(); closeConfirmModal(); }); {% endblock %}