Files
magico.prototype/prototype/company/app-permissions-acl.php

425 lines
24 KiB
PHP

<?php
$enablePrototypeComments = true;
include '../../header-invoice.php';
?>
<div class="container-xxl flex-grow-1 container-p-y">
<h4 class="fw-bold py-3 mb-4">
<span class="text-muted fw-light">Zarządzanie zespołem / <a href="app-permissions.php"
class="text-muted text-decoration-none">Uprawnienia</a> /</span> invoice.magico (ACL)
</h4>
<!-- Nagłówek kontekstu -->
<div class="card mb-4">
<div class="card-body d-flex flex-column flex-md-row align-items-md-center justify-content-between">
<div class="d-flex align-items-center mb-3 mb-md-0">
<div
class="avatar avatar-lg bg-label-primary rounded p-2 me-3 d-flex align-items-center justify-content-center">
<i class="bx bx-receipt fs-2"></i>
</div>
<div>
<h5 class="mb-1 fw-bold">invoice.magico</h5>
<p class="text-muted small mb-0">Zarządzaj macierzą uprawnień systemowych (ACL) dla modułu
księgowego. Każda kolumna to Grupa a każdy wiersz to konkretna funkcja dostępna w aplikacji.
Właściciel posiada niemodyfikowalny, pełny dostęp.</p>
</div>
</div>
<div class="ms-md-4">
<button class="btn btn-outline-secondary" onclick="window.location.href='app-groups.php'">
<i class="bx bx-group me-1"></i> Zarządzaj grupami
</button>
</div>
</div>
</div>
<!-- Tabela Macierzy (ACL Matrix) -->
<div class="card">
<div
class="card-header border-bottom d-flex flex-column flex-md-row justify-content-between align-items-md-center">
<h5 class="m-0 mb-3 mb-md-0">Macierz uprawnień</h5>
<div class="d-flex flex-wrap gap-2 flex-grow-1 ms-md-4 justify-content-end">
<div class="dropdown flex-grow-1" style="max-width: 250px;">
<button
class="btn btn-outline-secondary dropdown-toggle w-100 text-start d-flex justify-content-between align-items-center"
type="button" id="columnFilterDropdown" data-bs-toggle="dropdown" data-bs-auto-close="outside"
aria-expanded="false">
<span><i class="bx bx-filter-alt me-1"></i> Wybierz grupy</span>
</button>
<div class="dropdown-menu p-3" aria-labelledby="columnFilterDropdown" style="min-width: 250px;">
<h6 class="dropdown-header px-0 text-uppercase fw-bold pt-0">Wyświetlane kolumny (max 5)</h6>
<div class="form-check mb-2">
<input class="form-check-input column-toggle" type="checkbox" value="1" id="col_1" checked>
<label class="form-check-label" for="col_1">Właściciel </label>
</div>
<div class="form-check mb-2">
<input class="form-check-input column-toggle" type="checkbox" value="2" id="col_2" checked>
<label class="form-check-label" for="col_2">Kadra Kierownicza</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input column-toggle" type="checkbox" value="3" id="col_3" checked>
<label class="form-check-label" for="col_3">Dział Marketingu</label>
</div>
<div class="form-check">
<input class="form-check-input column-toggle" type="checkbox" value="4" id="col_4" checked>
<label class="form-check-label" for="col_4">Dział Obsługi Klienta</label>
</div>
</div>
</div>
<div class="input-group input-group-merge" style="max-width: 250px;">
<span class="input-group-text"><i class="bx bx-search"></i></span>
<input type="text" class="form-control form-control-sm" placeholder="Szukaj uprawnienia...">
</div>
</div>
</div>
<div class="table-responsive text-nowrap pb-2">
<!-- Dodana klasa acl-table dla łatwiejszego stylowania przez JS -->
<table class="table table-hover table-bordered mb-0 acl-table">
<thead class="table-light align-middle sticky-top" style="z-index: 10;">
<tr>
<th style="min-width: 250px;">Moduł i Nazwa Uprawnienia</th>
<th class="text-center" style="width: 150px;">
<i class="bx bx-crown text-warning fs-5 mb-1 d-block"></i>
Właściciel <br>
<span class="badge bg-label-secondary mt-1">N/D</span>
</th>
<th class="text-center" style="width: 150px;">
<i class="bx bx-briefcase text-primary fs-5 mb-1 d-block"></i>
Kadra Kierownicza
</th>
<th class="text-center" style="width: 150px;">
<i class="bx bx-broadcast text-info fs-5 mb-1 d-block"></i>
Dział Marketingu
</th>
<th class="text-center" style="width: 150px;">
<i class="bx bx-support text-success fs-5 mb-1 d-block"></i>
Dział Obsługi Klienta
</th>
</tr>
</thead>
<tbody class="table-border-bottom-0">
<!-- SEKCJA: GŁÓWNE -->
<tr class="table-secondary border-top-2 border-bottom-2">
<td colspan="5" class="fw-bold fs-6 text-uppercase tracking-wider">
<i class="bx bx-power-off me-2 text-muted"></i> Dostęp do modułu
</td>
</tr>
<tr class="global-access-row">
<td>
<h6 class="mb-0 text-body">Dostęp do aplikacji</h6>
<small class="text-muted">Główny włącznik modułu dla tej grupy. Wyłączenie blokuje poniższe
opcje.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0 global-access-checkbox" type="checkbox" checked
style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0 global-access-checkbox" type="checkbox"
style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0 global-access-checkbox" type="checkbox" checked
style="cursor: pointer;"></div>
</td>
</tr>
<!-- SEKCJA: FAKTURY -->
<tr class="table-secondary border-top-2 border-bottom-2">
<td colspan="5" class="fw-bold fs-6 text-uppercase tracking-wider">
<i class="bx bx-file me-2 text-muted"></i> Dokumenty Sprzedaży (Faktury)
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Odczyt plików i listy faktur</h6>
<small class="text-muted">Pozwala m.in przeglądać ekran głównego listingu.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Wystawianie nowych</h6>
<small class="text-muted">Ręczne kreowanie faktur przez formularz.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Trwałe usunięcie (Delete)</h6>
<small class="text-muted">Prawo do kasacji dokumentów z bazy danych.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
</tr>
<!-- SEKCJA: BAZA KONTRAHENTÓW -->
<tr class="table-secondary border-top-2 border-bottom-2">
<td colspan="5" class="fw-bold fs-6 text-uppercase tracking-wider">
<i class="bx bx-buildings me-2 text-muted"></i> Baza Kontrahentów (CRM)
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Przeglądanie kontrahentów</h6>
<small class="text-muted">Podstawowy dostęp do modułu z listą firm.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Dodawanie i Edycja</h6>
<small class="text-muted">Aktualizowanie adresów i nr NIP klientów.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
</tr>
<!-- SEKCJA: USTAWIENIA KSIĘGOWE -->
<tr class="table-secondary border-top-2 border-bottom-2">
<td colspan="5" class="fw-bold fs-6 text-uppercase tracking-wider text-danger">
<i class="bx bx-cog me-2 text-danger"></i> Krytyczne (Ustawienia Główne)
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Edycja numeracji i rejestrów</h6>
<small class="text-muted">Pozwala edytować schematy np: FV/2026/XX.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
</tr>
<tr>
<td>
<h6 class="mb-0 text-body">Eksport pliku JPK_V7</h6>
<small class="text-muted">Przesyłanie danych do Urzędu Skarbowego.</small>
</td>
<td class="text-center align-middle bg-label-secondary"><i
class="bx bx-check text-muted fs-4"></i></td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" checked style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
<td class="text-center align-middle">
<div class="d-flex justify-content-center align-items-center"><input
class="form-check-input m-0" type="checkbox" style="cursor: pointer;"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Sticky Save Footer -->
<div id="footer" class="footer" style="background: #e7e7ff; position: sticky; bottom: 0px; right: 0px; z-index: 1000;">
<footer class="content-footer footer border-top">
<div class="container-xxl d-flex py-3 justify-content-center">
<a href="app-permissions.php" class="btn btn-label-secondary btn-lg me-3">Anuluj</a>
<button class="btn btn-primary btn-lg px-5">Zapisz</button>
</div>
</footer>
</div>
<div class="content-backdrop fade"></div>
<?php include '../../footer.php'; ?>
<!-- Scripts -->
<script>
document.addEventListener("DOMContentLoaded", function () {
// Podobnie jak z członkami grup, uczyńmy całe komórki klikalnymi!
const aclTds = document.querySelectorAll('.acl-table tbody tr td.text-center');
aclTds.forEach(td => {
// Pomijamy komórki szare/zablokowane (Oznaczyliśmy Właściciela jako bg-label-secondary)
if (!td.classList.contains('bg-label-secondary')) {
td.style.cursor = 'pointer';
td.addEventListener('click', function (e) {
if (e.target.tagName !== 'INPUT') {
const checkbox = this.querySelector('.form-check-input');
// Zmieniamy tylko jeśli checkbox nie jest zablokowany (disabled)
if (checkbox && !checkbox.disabled) {
checkbox.checked = !checkbox.checked;
checkbox.dispatchEvent(new Event('change'));
}
}
});
// Sprawdzanie czy w komórce jest Główny Włącznik, aby odciąć resztę w dół
const accessCheckbox = td.querySelector('.form-check-input.global-access-checkbox');
if (accessCheckbox) {
accessCheckbox.addEventListener('change', function () {
toggleColumnAccess(td.cellIndex, this.checked);
});
// Wymuś stan po wczytaniu strony, jeśli aplikacja jest odznaczona (np. marketing)
if (!accessCheckbox.checked) {
toggleColumnAccess(td.cellIndex, false);
}
}
}
});
// Funkcja blokowania/odblokowania całej kolumny
function toggleColumnAccess(colIndex, isEnabled) {
const allRows = document.querySelectorAll('.acl-table tbody tr');
allRows.forEach(row => {
// Pomijamy szare wiersze podziału oraz sam wiersz głównego włącznika
if (!row.classList.contains('table-secondary') && !row.classList.contains('global-access-row') && row.cells.length > colIndex) {
const cell = row.cells[colIndex];
const checkbox = cell.querySelector('.form-check-input');
if (checkbox && !cell.classList.contains('bg-label-secondary')) {
if (!isEnabled) {
checkbox.checked = false;
checkbox.disabled = true;
cell.style.opacity = '0.4';
cell.style.pointerEvents = 'none'; // cała komórka staje się nieklikalna
} else {
checkbox.disabled = false;
cell.style.opacity = '1';
cell.style.pointerEvents = 'auto'; // przywraca klikalność
}
}
}
});
}
// Logika wyświetlania/ukrywania kolumn grupowych
const maxColumns = 5; // Mamy w sumie 1(nazwa) + 4 (grupy). Limit 5 grup oznacza max 6 kolumn tabeli. Zostawiłem zmienną na przyszłość.
const columnToggles = document.querySelectorAll('.column-toggle:not(:disabled)');
columnToggles.forEach(toggle => {
toggle.addEventListener('change', function () {
const checkedCount = document.querySelectorAll('.column-toggle:checked').length;
// Zabezpieczenie limitu 5 (wliczając właściciela)
if (this.checked && checkedCount > 5) {
this.checked = false;
alert('Możesz wybrać maksymalnie 5 grup do jednoczesnego wyświetlania.');
return;
}
const colIndex = parseInt(this.value);
const isVisible = this.checked;
// Ukrywamy nagłówek
const theadRow = document.querySelector('.acl-table thead tr');
if (theadRow && theadRow.cells[colIndex]) {
theadRow.cells[colIndex].style.display = isVisible ? '' : 'none';
}
// Ukrywamy komórki w ciele tabeli
const tbodyRows = document.querySelectorAll('.acl-table tbody tr');
tbodyRows.forEach(row => {
// Wiersze sekcji (szare) mają colspan=5, trzeba go dynamicznie zaktualizować
if (row.classList.contains('table-secondary')) {
const th = row.querySelector('td');
// Liczymy widoczne kolumny (nagłówek)
const visibleCols = document.querySelectorAll('.acl-table thead th:not([style*="display: none"])').length;
if (th) th.setAttribute('colspan', visibleCols || 2);
} else if (row.cells[colIndex]) {
row.cells[colIndex].style.display = isVisible ? '' : 'none';
}
});
});
});
});
</script>
</body>
</html>