rozbicie na moduły, poprawki i komendy cli
This commit is contained in:
440
zbiorka_app/static/css/custom.css
Normal file
440
zbiorka_app/static/css/custom.css
Normal file
@@ -0,0 +1,440 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
|
||||
|
||||
/* ========= TOKENS ========= */
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
|
||||
--bg: #121212;
|
||||
/* główne tło */
|
||||
--surface-0: #1a1a1a;
|
||||
/* navbar, header */
|
||||
--surface-1: #202020;
|
||||
/* karty */
|
||||
--surface-2: #2a2a2a;
|
||||
/* nagłówki kart, ciemniejsze sekcje */
|
||||
--border: #3a3a3a;
|
||||
|
||||
--text: #e4e4e4;
|
||||
--text-muted: #a8a8a8;
|
||||
|
||||
--accent: #f5c84c;
|
||||
/* żółty/amber akcent */
|
||||
--accent-600: #e3b23f;
|
||||
--accent-700: #cfa033;
|
||||
--accent-300: #ffe083;
|
||||
|
||||
--radius: 10px;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, .5);
|
||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, .45);
|
||||
--trans: 220ms cubic-bezier(.2, .8, .2, 1);
|
||||
}
|
||||
|
||||
/* ========= BASE ========= */
|
||||
body {
|
||||
font-family: 'Roboto', system-ui, -apple-system, Segoe UI, Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
padding-top: 1vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: color var(--trans);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--accent-300);
|
||||
}
|
||||
|
||||
/* ========= NAVBAR ========= */
|
||||
.navbar {
|
||||
background: var(--surface-0);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
transition: color var(--trans);
|
||||
}
|
||||
|
||||
.navbar-brand:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted);
|
||||
transition: color var(--trans);
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link:focus {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* ========= CARDS ========= */
|
||||
.card {
|
||||
background: var(--surface-1);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
margin-bottom: 20px;
|
||||
transition: transform 160ms ease, box-shadow 160ms ease, border-color var(--trans);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: color-mix(in srgb, var(--accent) 20%, var(--border));
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: var(--surface-2);
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Wyróżniona karta */
|
||||
.card.wspomoz-card {
|
||||
border: 1px solid var(--accent) !important;
|
||||
border-radius: var(--radius) !important;
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 20%, transparent);
|
||||
}
|
||||
|
||||
.card.wspomoz-card .card-body,
|
||||
.card.wspomoz-card .card-title,
|
||||
.card.wspomoz-card .card-text {
|
||||
color: var(--text) !important;
|
||||
font-weight: 700;
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
/* ========= BUTTONS ========= */
|
||||
.btn {
|
||||
font-weight: 700;
|
||||
border-radius: 8px;
|
||||
transition: transform 120ms ease, background-color var(--trans), border-color var(--trans), color var(--trans);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn:focus-visible {
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 40%, transparent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Primary = żółty */
|
||||
.btn-primary {
|
||||
background-color: var(--accent);
|
||||
border-color: var(--accent-600);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--accent-600);
|
||||
border-color: var(--accent-700);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.btn-primary:active,
|
||||
.btn-primary:focus {
|
||||
background-color: var(--accent-700) !important;
|
||||
border-color: var(--accent-700) !important;
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
/* Secondary = ciemna szarość */
|
||||
.btn-secondary,
|
||||
.btn-outline-primary {
|
||||
background: var(--surface-1);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.btn-secondary:hover,
|
||||
.btn-outline-primary:hover {
|
||||
border-color: var(--accent-600);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* ========= PROGRESS ========= */
|
||||
.progress {
|
||||
background: var(--surface-2);
|
||||
border-radius: 999px;
|
||||
height: 14px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
--progress-width: 0%;
|
||||
width: var(--progress-width);
|
||||
background: linear-gradient(90deg, var(--accent-600), var(--accent));
|
||||
transition: width var(--trans);
|
||||
}
|
||||
|
||||
/* ========= ALERTS ========= */
|
||||
|
||||
|
||||
/* ALERT VARIANTS */
|
||||
.alert-success {
|
||||
background: rgba(40, 167, 69, 0.15);
|
||||
/* lekka zieleń */
|
||||
border-color: #28a745;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: rgba(220, 53, 69, 0.15);
|
||||
/* lekka czerwień */
|
||||
border-color: #dc3545;
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(255, 193, 7, 0.15);
|
||||
/* lekki bursztyn */
|
||||
border-color: #ffc107;
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(23, 162, 184, 0.15);
|
||||
/* lekki cyjan */
|
||||
border-color: #17a2b8;
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= TYPO ========= */
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: .6rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: .5rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
small,
|
||||
.text-muted {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
/* ========= RESPONSIVE ========= */
|
||||
.container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: .95rem;
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
z-index: 1080;
|
||||
}
|
||||
|
||||
/* ponad kartą/tabelą */
|
||||
@media (max-width: 576px) {
|
||||
.table-responsive {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========= FORMS ========= */
|
||||
input.form-control,
|
||||
textarea.form-control,
|
||||
select.form-select {
|
||||
background-color: var(--surface-1);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
input.form-control:focus,
|
||||
textarea.form-control:focus,
|
||||
select.form-select:focus {
|
||||
background-color: var(--surface-1);
|
||||
border-color: var(--accent-600);
|
||||
color: var(--text);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 50%, transparent);
|
||||
}
|
||||
|
||||
/* pole edycji ciemne */
|
||||
.CodeMirror {
|
||||
background-color: #1e1e1e !important;
|
||||
color: #e0e0e0 !important;
|
||||
}
|
||||
|
||||
/* kursor */
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid #e0e0e0 !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active {
|
||||
background: var(--accent);
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* sticky tylko od md wzwyż, by na mobile nie przyklejać */
|
||||
@media (min-width: 768px) {
|
||||
.sticky-md {
|
||||
position: sticky;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--sticky-offset: 1rem;
|
||||
}
|
||||
|
||||
/* Rząd kopiowania: czytelny, łatwy klik w przycisk */
|
||||
.copy-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-rows: auto auto;
|
||||
gap: .25rem .5rem;
|
||||
align-items: center;
|
||||
padding: .5rem .75rem;
|
||||
border: 1px solid var(--border, rgba(255, 255, 255, .15));
|
||||
border-radius: .75rem;
|
||||
background: rgba(255, 255, 255, .02);
|
||||
}
|
||||
|
||||
.copy-row+.copy-row {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
|
||||
.copy-row__label {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 2;
|
||||
font-weight: 600;
|
||||
font-size: .9rem;
|
||||
opacity: .85;
|
||||
}
|
||||
|
||||
.copy-row__value {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 2 / 3;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
||||
letter-spacing: .02em;
|
||||
user-select: text;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.copy-row__btn {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 3;
|
||||
height: fit-content;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
@media (max-width: 575.98px) {
|
||||
|
||||
/* na XS przycisk pod spodem – łatwiej trafić kciukiem */
|
||||
.copy-row {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
}
|
||||
|
||||
.copy-row__btn {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 3 / 4;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.hr-bw {
|
||||
border: 0;
|
||||
border-top: 1px dashed rgba(255, 255, 255, 0.2);
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* Tylko ten przycisk */
|
||||
.btn.btn-outline-light.btn-opis {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--border);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.btn.btn-outline-light.btn-opis:hover,
|
||||
.btn.btn-outline-light.btn-opis:focus {
|
||||
color: #fff;
|
||||
background-color: #161616;
|
||||
border-color: color-mix(in srgb, var(--accent) 20%, var(--border));
|
||||
}
|
||||
|
||||
.btn.btn-outline-light {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.btn.btn-outline-light:hover,
|
||||
.btn.btn-outline-light:focus {
|
||||
color: #fff;
|
||||
background-color: #161616;
|
||||
border-color: color-mix(in srgb, var(--accent) 20%, #ffffff);
|
||||
}
|
||||
|
||||
.btn.btn-outline-light:active {
|
||||
color: #fff;
|
||||
background-color: #141414;
|
||||
border-color: color-mix(in srgb, var(--accent) 24%, #ffffff);
|
||||
}
|
||||
|
||||
#kanalyWarning,
|
||||
#postepyWarning {
|
||||
border: 1px solid #ffc107;
|
||||
background-color: #2c2c2c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
input:disabled,
|
||||
textarea:disabled,
|
||||
select:disabled {
|
||||
background-color: #2b2b2b !important;
|
||||
color: #bbb !important;
|
||||
opacity: 1 !important;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
0
zbiorka_app/static/js/admin_dashboard.js
Normal file
0
zbiorka_app/static/js/admin_dashboard.js
Normal file
21
zbiorka_app/static/js/dodaj_wplate.js
Normal file
21
zbiorka_app/static/js/dodaj_wplate.js
Normal file
@@ -0,0 +1,21 @@
|
||||
(function () {
|
||||
const kwota = document.getElementById('kwota');
|
||||
const opis = document.getElementById('opis');
|
||||
const opisCount = document.getElementById('opisCount');
|
||||
|
||||
document.querySelectorAll('.btn-kwota').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const val = btn.getAttribute('data-amount');
|
||||
if (val && kwota) {
|
||||
kwota.value = Number(val).toFixed(2);
|
||||
kwota.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (opis && opisCount) {
|
||||
const updateCount = () => opisCount.textContent = opis.value.length.toString();
|
||||
opis.addEventListener('input', updateCount);
|
||||
updateCount();
|
||||
}
|
||||
})();
|
||||
5
zbiorka_app/static/js/dodaj_wydatek.js
Normal file
5
zbiorka_app/static/js/dodaj_wydatek.js
Normal file
@@ -0,0 +1,5 @@
|
||||
document.addEventListener('input', (e) => {
|
||||
if (e.target && e.target.id === 'opis') {
|
||||
document.getElementById('opisCount').textContent = e.target.value.length;
|
||||
}
|
||||
});
|
||||
88
zbiorka_app/static/js/edytuj_stan.js
Normal file
88
zbiorka_app/static/js/edytuj_stan.js
Normal file
@@ -0,0 +1,88 @@
|
||||
(() => {
|
||||
// Root kontenera z danymi (dataset.cel)
|
||||
const root = document.querySelector('[data-module="edit-stan"]');
|
||||
if (!root) return;
|
||||
|
||||
const input = root.querySelector('#stan');
|
||||
const previewPct = root.querySelector('#previewPct');
|
||||
const previewBar = root.querySelector('#previewBar');
|
||||
const previewNote = root.querySelector('#previewNote');
|
||||
|
||||
// Cel przekazany jako data atrybut
|
||||
const cel = Number(root.dataset.cel || 0);
|
||||
|
||||
function clamp(n) {
|
||||
if (Number.isNaN(n)) return 0;
|
||||
return n < 0 ? 0 : n;
|
||||
}
|
||||
|
||||
function pct(val) {
|
||||
if (!cel || cel <= 0) return 0;
|
||||
return (val / cel) * 100;
|
||||
}
|
||||
|
||||
function updatePreview() {
|
||||
if (!input) return;
|
||||
const val = clamp(Number(input.value));
|
||||
const p = Math.max(0, Math.min(100, pct(val)));
|
||||
|
||||
if (previewPct) previewPct.textContent = p.toFixed(1);
|
||||
|
||||
if (previewBar) {
|
||||
previewBar.style.width = p + '%';
|
||||
previewBar.setAttribute('aria-valuenow', p.toFixed(2));
|
||||
}
|
||||
|
||||
if (previewNote) {
|
||||
if (cel > 0) {
|
||||
const diff = cel - val;
|
||||
const isZero = Math.abs(diff) < 0.005; // float-safe
|
||||
if (diff > 0 && !isZero) {
|
||||
previewNote.textContent = 'Do celu brakuje: ' + diff.toFixed(2) + ' PLN';
|
||||
} else if (isZero) {
|
||||
previewNote.textContent = 'Cel osiągnięty.';
|
||||
} else {
|
||||
previewNote.textContent = 'Przekroczono cel o: ' + Math.abs(diff).toFixed(2) + ' PLN';
|
||||
}
|
||||
} else {
|
||||
previewNote.textContent = 'Brak zdefiniowanego celu — procent nie jest wyliczany.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Zmiana ręczna
|
||||
if (input) {
|
||||
input.addEventListener('input', updatePreview);
|
||||
input.addEventListener('change', () => {
|
||||
if (Number(input.value) < 0) input.value = '0.00';
|
||||
updatePreview();
|
||||
});
|
||||
}
|
||||
|
||||
// Przyciski +/- delta
|
||||
root.querySelectorAll('.btn-delta').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const d = Number(btn.getAttribute('data-delta') || 0);
|
||||
const cur = Number(input?.value || 0);
|
||||
if (!input) return;
|
||||
input.value = clamp(cur + d).toFixed(2);
|
||||
updatePreview();
|
||||
input.focus();
|
||||
});
|
||||
});
|
||||
|
||||
// Ustaw na konkretną wartość
|
||||
root.querySelectorAll('.btn-set').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const v = Number(btn.getAttribute('data-value') || 0);
|
||||
if (!input) return;
|
||||
input.value = clamp(v).toFixed(2);
|
||||
updatePreview();
|
||||
input.focus();
|
||||
});
|
||||
});
|
||||
|
||||
// Inicjalny podgląd
|
||||
updatePreview();
|
||||
})();
|
||||
19
zbiorka_app/static/js/formularz_rezerwy.js
Normal file
19
zbiorka_app/static/js/formularz_rezerwy.js
Normal file
@@ -0,0 +1,19 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const uzyjKonta = document.getElementById('uzyj_konta');
|
||||
const kontoField = document.getElementById('konto-field');
|
||||
const uzyjBlik = document.getElementById('uzyj_blik');
|
||||
const blikField = document.getElementById('blik-field');
|
||||
|
||||
if (uzyjKonta && kontoField) {
|
||||
uzyjKonta.addEventListener('change', function() {
|
||||
kontoField.style.display = this.checked ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
if (uzyjBlik && blikField) {
|
||||
uzyjBlik.addEventListener('change', function() {
|
||||
blikField.style.display = this.checked ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
63
zbiorka_app/static/js/formularz_zbiorek.js
Normal file
63
zbiorka_app/static/js/formularz_zbiorek.js
Normal file
@@ -0,0 +1,63 @@
|
||||
(function () {
|
||||
// Licznik znaków opisu
|
||||
const opis = document.getElementById('opis');
|
||||
const opisCount = document.getElementById('opisCount');
|
||||
if (opis && opisCount) {
|
||||
const updateCount = () => (opisCount.textContent = String(opis.value.length));
|
||||
opis.addEventListener('input', updateCount);
|
||||
updateCount();
|
||||
}
|
||||
|
||||
// IBAN: tylko cyfry, auto-grupowanie co 4
|
||||
const iban = document.getElementById('numer_konta');
|
||||
if (iban) {
|
||||
iban.addEventListener('input', () => {
|
||||
const digits = iban.value.replace(/\D/g, '').slice(0, 26); // 26 cyfr po PL
|
||||
const chunked = digits.replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.value = chunked;
|
||||
});
|
||||
}
|
||||
|
||||
// BLIK telefon: tylko cyfry, format 3-3-3
|
||||
const tel = document.getElementById('numer_telefonu_blik');
|
||||
if (tel) {
|
||||
tel.addEventListener('input', () => {
|
||||
const digits = tel.value.replace(/\D/g, '').slice(0, 9);
|
||||
const parts = [];
|
||||
if (digits.length > 0) parts.push(digits.substring(0, 3));
|
||||
if (digits.length > 3) parts.push(digits.substring(3, 6));
|
||||
if (digits.length > 6) parts.push(digits.substring(6, 9));
|
||||
tel.value = parts.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
// „Ustaw globalne” – jest tylko w trybie edycji; odpalamy warunkowo
|
||||
const setGlobalBtn = document.getElementById('ustaw-globalne');
|
||||
if (setGlobalBtn && iban && tel) {
|
||||
setGlobalBtn.addEventListener('click', () => {
|
||||
const gIban = setGlobalBtn.dataset.iban || '';
|
||||
const gBlik = setGlobalBtn.dataset.blik || '';
|
||||
|
||||
if (gIban) {
|
||||
iban.value = gIban.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.dispatchEvent(new Event('input'));
|
||||
}
|
||||
if (gBlik) {
|
||||
const d = gBlik.replace(/\D/g, '').slice(0, 9);
|
||||
const p = [d.slice(0, 3), d.slice(3, 6), d.slice(6, 9)]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
tel.value = p;
|
||||
tel.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cel: minimalna wartość
|
||||
const cel = document.getElementById('cel');
|
||||
if (cel) {
|
||||
cel.addEventListener('change', () => {
|
||||
if (cel.value && Number(cel.value) < 0.01) cel.value = '0.01';
|
||||
});
|
||||
}
|
||||
})();
|
||||
153
zbiorka_app/static/js/kwoty_formularz.js
Normal file
153
zbiorka_app/static/js/kwoty_formularz.js
Normal file
@@ -0,0 +1,153 @@
|
||||
(function () {
|
||||
const tbody = document.querySelector('#produkty-body');
|
||||
const celInput = document.querySelector('#cel');
|
||||
const box = document.querySelector('#celSyncBox');
|
||||
const msg = document.querySelector('#celSyncMsg');
|
||||
const btn = document.querySelector('#btnApplyCelFromSum');
|
||||
|
||||
if (!tbody || !celInput || !box || !msg || !btn) return;
|
||||
|
||||
const EPS = 0.01; // tolerancja porównania
|
||||
|
||||
function parsePrice(raw) {
|
||||
if (!raw) return NaN;
|
||||
const s = String(raw).trim().replace(/\s+/g, '').replace(',', '.');
|
||||
const n = Number(s);
|
||||
return Number.isFinite(n) && n >= 0 ? n : NaN;
|
||||
}
|
||||
|
||||
function getRows() {
|
||||
return Array.from(tbody.querySelectorAll('tr'));
|
||||
}
|
||||
|
||||
function computeSum() {
|
||||
const rows = getRows();
|
||||
|
||||
let hasNamed = false;
|
||||
let sumAll = 0; // suma ze wszystkich wierszy z nazwą i poprawną ceną
|
||||
let sumToBuy = 0; // suma tylko z wierszy NIE oznaczonych jako "Kupione"
|
||||
|
||||
for (const tr of rows) {
|
||||
const nameInput = tr.querySelector('input[name="item_nazwa[]"]');
|
||||
const priceInput = tr.querySelector('input[name="item_cena[]"]');
|
||||
const kupioneSwitch = tr.querySelector('.kupione-switch');
|
||||
|
||||
const name = nameInput ? nameInput.value.trim() : '';
|
||||
if (!name) continue; // ignoruj puste wiersze bez nazwy
|
||||
|
||||
hasNamed = true;
|
||||
|
||||
const priceVal = priceInput ? parsePrice(priceInput.value) : NaN;
|
||||
if (Number.isNaN(priceVal)) continue;
|
||||
|
||||
// zawsze dolicz do sumy wszystkich
|
||||
sumAll += priceVal;
|
||||
|
||||
// do sumy do-kupienia tylko jeśli nie jest oznaczone jako kupione
|
||||
if (!(kupioneSwitch && kupioneSwitch.checked)) {
|
||||
sumToBuy += priceVal;
|
||||
}
|
||||
}
|
||||
|
||||
return { hasNamed, sumAll, sumToBuy };
|
||||
}
|
||||
|
||||
|
||||
function readCel() {
|
||||
const v = parsePrice(celInput.value);
|
||||
return Number.isNaN(v) ? null : v;
|
||||
}
|
||||
|
||||
function formatPln(n) {
|
||||
// Nie narzucamy locale – prosto 2 miejsca
|
||||
return n.toFixed(2);
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
const { hasNamed, sumAll, sumToBuy } = computeSum();
|
||||
|
||||
// Brak produktów (brak nazw) lub obie sumy = 0 → nic nie pokazuj
|
||||
if (!hasNamed || (sumAll <= 0 && sumToBuy <= 0)) {
|
||||
box.classList.add('d-none');
|
||||
btn.classList.add('d-none');
|
||||
box.classList.remove('alert-success', 'alert-info');
|
||||
msg.textContent = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const cel = readCel();
|
||||
const target = sumToBuy; // porównujemy do kwoty POZOSTAŁE DO KUPIENIA
|
||||
|
||||
// Jeśli cel nie ustawiony lub NaN → zaproponuj ustawienie celu = sumToBuy
|
||||
if (cel === null) {
|
||||
box.classList.remove('d-none');
|
||||
box.classList.remove('alert-success');
|
||||
box.classList.add('alert-info');
|
||||
|
||||
// pokazujemy obie sumy w komunikacie
|
||||
msg.innerHTML = `
|
||||
<div>Wszystkie: <strong>${formatPln(sumAll)} PLN</strong> ·
|
||||
Do kupienia: <strong>${formatPln(sumToBuy)} PLN</strong></div>
|
||||
<div class="mt-1">Możesz ustawić <strong>cel</strong> na kwotę do kupienia.</div>
|
||||
`;
|
||||
btn.textContent = `Ustaw cel = ${formatPln(target)} PLN`;
|
||||
btn.classList.remove('d-none');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mamy cel — porównanie do sumy do-kupienia
|
||||
if (Math.abs(cel - target) <= EPS) {
|
||||
box.classList.remove('d-none');
|
||||
box.classList.remove('alert-info');
|
||||
box.classList.add('alert-success');
|
||||
msg.innerHTML = `
|
||||
Suma <em>do kupienia</em> (<strong>${formatPln(target)} PLN</strong>) jest równa celowi.
|
||||
<div class="small text-muted mt-1">Wszystkie: ${formatPln(sumAll)} PLN · Do kupienia: ${formatPln(sumToBuy)} PLN</div>
|
||||
`;
|
||||
btn.classList.add('d-none');
|
||||
} else {
|
||||
box.classList.remove('d-none');
|
||||
box.classList.remove('alert-success');
|
||||
box.classList.add('alert-info');
|
||||
msg.innerHTML = `
|
||||
<div>Wszystkie: <strong>${formatPln(sumAll)} PLN</strong> ·
|
||||
Do kupienia: <strong>${formatPln(sumToBuy)} PLN</strong></div>
|
||||
<div class="mt-1">Cel: <strong>${formatPln(cel)} PLN</strong></div>
|
||||
`;
|
||||
btn.textContent = `Zaktualizuj cel do ${formatPln(target)} PLN`;
|
||||
btn.classList.remove('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const { sumToBuy } = computeSum();
|
||||
if (sumToBuy > 0) {
|
||||
celInput.value = formatPln(sumToBuy);
|
||||
celInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
celInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
|
||||
// Reaguj na zmiany cen/nazw
|
||||
tbody.addEventListener('input', (e) => {
|
||||
const name = e.target.getAttribute('name');
|
||||
if (name === 'item_nazwa[]' || name === 'item_cena[]') {
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
|
||||
// Reaguj na zmiany celu
|
||||
celInput.addEventListener('input', updateUI);
|
||||
celInput.addEventListener('change', updateUI);
|
||||
|
||||
// Obserwuj dodawanie/usuwanie wierszy przez inne skrypty
|
||||
const mo = new MutationObserver(() => updateUI());
|
||||
mo.observe(tbody, { childList: true, subtree: true });
|
||||
|
||||
// Init po załadowaniu
|
||||
document.addEventListener('DOMContentLoaded', updateUI);
|
||||
// i jedno wywołanie na starcie (gdy DOMContentLoaded już był)
|
||||
updateUI();
|
||||
})();
|
||||
4
zbiorka_app/static/js/mde_custom.js
Normal file
4
zbiorka_app/static/js/mde_custom.js
Normal file
@@ -0,0 +1,4 @@
|
||||
var simplemde = new SimpleMDE({
|
||||
element: document.getElementById("opis"),
|
||||
forceSync: true
|
||||
});
|
||||
73
zbiorka_app/static/js/produkty_formularz.js
Normal file
73
zbiorka_app/static/js/produkty_formularz.js
Normal file
@@ -0,0 +1,73 @@
|
||||
(function () {
|
||||
const body = document.querySelector('#produkty-body');
|
||||
const addBtn = document.querySelector('#add-row');
|
||||
const clearBtn = document.querySelector('#clear-empty');
|
||||
|
||||
if (!body) return;
|
||||
|
||||
function reindexHidden() {
|
||||
const rows = [...body.querySelectorAll('tr')];
|
||||
rows.forEach((tr, idx) => {
|
||||
const hidden = tr.querySelector('input[type="hidden"][name^="item_kupione_val_"]');
|
||||
if (hidden) hidden.name = `item_kupione_val_${idx}`;
|
||||
});
|
||||
}
|
||||
|
||||
function makeRow() {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td><input type="text" class="form-control" name="item_nazwa[]" placeholder="np. Karma Brit 10kg" required></td>
|
||||
<td><input type="url" class="form-control" name="item_link[]" placeholder="https://..."></td>
|
||||
<td><input type="text" inputmode="decimal" class="form-control text-end" name="item_cena[]" placeholder="0,00"></td>
|
||||
<td>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input kupione-switch" type="checkbox">
|
||||
<input type="hidden" name="item_kupione_val_TMP" value="0">
|
||||
<label class="form-check-label small">Do kupienia</label>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<button type="button" class="btn btn-sm btn-outline-light border remove-row" title="Usuń wiersz">✕</button>
|
||||
</td>`;
|
||||
return tr;
|
||||
}
|
||||
|
||||
body.addEventListener('change', (e) => {
|
||||
if (e.target.classList.contains('kupione-switch')) {
|
||||
const tr = e.target.closest('tr');
|
||||
const hidden = tr.querySelector('input[type="hidden"][name^="item_kupione_val_"]');
|
||||
const label = tr.querySelector('.form-check-label');
|
||||
if (hidden) hidden.value = e.target.checked ? '1' : '0';
|
||||
if (label) label.textContent = e.target.checked ? 'Kupione' : 'Do kupienia';
|
||||
}
|
||||
});
|
||||
|
||||
body.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('remove-row')) {
|
||||
e.preventDefault();
|
||||
const tr = e.target.closest('tr');
|
||||
tr.remove();
|
||||
reindexHidden();
|
||||
}
|
||||
});
|
||||
|
||||
addBtn?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
body.appendChild(makeRow());
|
||||
reindexHidden();
|
||||
});
|
||||
|
||||
clearBtn?.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
[...body.querySelectorAll('tr')].forEach(tr => {
|
||||
const name = tr.querySelector('input[name="item_nazwa[]"]')?.value.trim();
|
||||
const link = tr.querySelector('input[name="item_link[]"]')?.value.trim();
|
||||
const cena = tr.querySelector('input[name="item_cena[]"]')?.value.trim();
|
||||
if (!name && !link && !cena) tr.remove();
|
||||
});
|
||||
reindexHidden();
|
||||
});
|
||||
|
||||
// startowa normalizacja nazw hiddenów (ważne w trybie edycji)
|
||||
reindexHidden();
|
||||
})();
|
||||
13
zbiorka_app/static/js/progress.js
Normal file
13
zbiorka_app/static/js/progress.js
Normal file
@@ -0,0 +1,13 @@
|
||||
function animateProgressBars() {
|
||||
document.querySelectorAll('.progress-bar').forEach(bar => {
|
||||
const progressValue = bar.getAttribute('aria-valuenow');
|
||||
bar.style.setProperty('--progress-width', progressBarWidth(progressBarValue(progressBar)));
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.progress-bar').forEach(bar => {
|
||||
const width = bar.getAttribute('aria-valuenow') + '%';
|
||||
bar.style.setProperty('--progress-width', width);
|
||||
});
|
||||
});
|
||||
74
zbiorka_app/static/js/przelaczniki_zabezpieczenie.js
Normal file
74
zbiorka_app/static/js/przelaczniki_zabezpieczenie.js
Normal file
@@ -0,0 +1,74 @@
|
||||
// static/js/przelaczniki_zabezpieczenie.js
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function onReady(cb) {
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', cb);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
onReady(function () {
|
||||
var boxes = Array.prototype.slice.call(
|
||||
document.querySelectorAll('input.form-check-input[type="checkbox"][data-group="postepy"]')
|
||||
);
|
||||
var warning = document.getElementById('postepyWarning');
|
||||
|
||||
if (!boxes.length || !warning) {
|
||||
// Nic do zrobienia, brak elementów
|
||||
return;
|
||||
}
|
||||
|
||||
function atLeastOneChecked() {
|
||||
return boxes.some(function (b) { return !!b.checked; });
|
||||
}
|
||||
|
||||
function showWarning(show) {
|
||||
warning.classList.toggle('d-none', !show);
|
||||
if (show) {
|
||||
// dyskretny highlight
|
||||
warning.classList.add('border', 'border-warning');
|
||||
warning.style.transition = 'box-shadow 0.2s ease';
|
||||
warning.style.boxShadow = '0 0 0.25rem rgba(255,193,7,.6)';
|
||||
setTimeout(function () {
|
||||
warning.style.boxShadow = '';
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
function enforceAtLeastOne(e) {
|
||||
// Jeżeli po zmianie byłaby 0/3, przywróć zaznaczenie klikniętego i pokaż ostrzeżenie
|
||||
if (!atLeastOneChecked()) {
|
||||
e.target.checked = true;
|
||||
showWarning(true);
|
||||
e.target.classList.add('is-invalid');
|
||||
setTimeout(function () { e.target.classList.remove('is-invalid'); }, 400);
|
||||
return;
|
||||
}
|
||||
// Jeśli >=1, ostrzeżenie ukryj
|
||||
showWarning(false);
|
||||
}
|
||||
|
||||
// Podpinka zdarzeń
|
||||
boxes.forEach(function (box) {
|
||||
box.addEventListener('change', enforceAtLeastOne);
|
||||
});
|
||||
|
||||
// Walidacja przy submit (na wszelki wypadek)
|
||||
var form = boxes[0].closest('form');
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
if (!atLeastOneChecked()) {
|
||||
e.preventDefault();
|
||||
showWarning(true);
|
||||
boxes[0].focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Inicjalny stan (np. po rerenderze z błędem)
|
||||
showWarning(!atLeastOneChecked());
|
||||
});
|
||||
})();
|
||||
0
zbiorka_app/static/js/service-worker.js
Normal file
0
zbiorka_app/static/js/service-worker.js
Normal file
88
zbiorka_app/static/js/sposoby_wplat.js
Normal file
88
zbiorka_app/static/js/sposoby_wplat.js
Normal file
@@ -0,0 +1,88 @@
|
||||
(function () {
|
||||
const form = document.getElementById('form-edit-zbiorka') || document.getElementById('form-add-zbiorka') || document.querySelector('form');
|
||||
|
||||
const map = [
|
||||
['uzyj_konta', 'numer_konta'],
|
||||
['uzyj_blik', 'numer_telefonu_blik']
|
||||
];
|
||||
|
||||
const warnBox = document.getElementById('kanalyWarning');
|
||||
|
||||
function showWarn(show) {
|
||||
if (!warnBox) return;
|
||||
warnBox.classList.toggle('d-none', !show);
|
||||
}
|
||||
|
||||
function getEl(id) { return document.getElementById(id); }
|
||||
|
||||
function toggleField(chkId, inputId) {
|
||||
const chk = getEl(chkId);
|
||||
const inp = getEl(inputId);
|
||||
if (!inp || !chk) return;
|
||||
const on = chk.checked;
|
||||
inp.disabled = !on;
|
||||
if (on) inp.setAttribute('required', '');
|
||||
else inp.removeAttribute('required');
|
||||
}
|
||||
|
||||
function atLeastOneOn() {
|
||||
return map.some(([c]) => getEl(c)?.checked);
|
||||
}
|
||||
|
||||
function blinkInvalid(el) {
|
||||
if (!el) return;
|
||||
el.classList.add('is-invalid');
|
||||
setTimeout(() => el.classList.remove('is-invalid'), 400);
|
||||
}
|
||||
|
||||
function preventUncheckLast(e) {
|
||||
const target = e.target;
|
||||
if (target.checked) return;
|
||||
const after = map.map(([c]) => c === target.id ? false : !!getEl(c)?.checked);
|
||||
if (!after.some(Boolean)) {
|
||||
e.preventDefault();
|
||||
target.checked = true;
|
||||
showWarn(true);
|
||||
blinkInvalid(target);
|
||||
} else {
|
||||
showWarn(false);
|
||||
}
|
||||
}
|
||||
|
||||
function onToggle(chkId, inputId) {
|
||||
toggleField(chkId, inputId);
|
||||
showWarn(!atLeastOneOn());
|
||||
}
|
||||
|
||||
map.forEach(([chkId, inputId]) => {
|
||||
const chk = getEl(chkId);
|
||||
if (!chk) return;
|
||||
chk.addEventListener('click', preventUncheckLast);
|
||||
chk.addEventListener('change', () => onToggle(chkId, inputId));
|
||||
toggleField(chkId, inputId);
|
||||
});
|
||||
showWarn(!atLeastOneOn());
|
||||
|
||||
if (form) {
|
||||
form.addEventListener('submit', function (e) {
|
||||
if (!atLeastOneOn()) {
|
||||
e.preventDefault();
|
||||
showWarn(true);
|
||||
blinkInvalid(getEl('uzyj_konta') || getEl('uzyj_blik'));
|
||||
(getEl('uzyj_konta') || getEl('uzyj_blik'))?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [chkId, inputId] of map) {
|
||||
const chk = getEl(chkId), inp = getEl(inputId);
|
||||
if (chk?.checked && inp && !inp.value.trim()) {
|
||||
e.preventDefault();
|
||||
showWarn(true);
|
||||
blinkInvalid(inp);
|
||||
inp.focus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
27
zbiorka_app/static/js/transakcje.js
Normal file
27
zbiorka_app/static/js/transakcje.js
Normal file
@@ -0,0 +1,27 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const modalW = new bootstrap.Modal(document.getElementById('modalWplata'));
|
||||
const modalX = new bootstrap.Modal(document.getElementById('modalWydatek'));
|
||||
|
||||
// WPŁATA
|
||||
document.querySelectorAll('.btn-edit-wplata').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const form = document.getElementById('formWplata');
|
||||
form.action = btn.dataset.action;
|
||||
document.getElementById('wplataKwota').value = btn.dataset.kwota || '';
|
||||
document.getElementById('wplataOpis').value = btn.dataset.opis || '';
|
||||
modalW.show();
|
||||
});
|
||||
});
|
||||
|
||||
// WYDATEK
|
||||
document.querySelectorAll('.btn-edit-wydatek').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const form = document.getElementById('formWydatek');
|
||||
form.action = btn.dataset.action;
|
||||
document.getElementById('wydatekKwota').value = btn.dataset.kwota || '';
|
||||
document.getElementById('wydatekOpis').value = btn.dataset.opis || '';
|
||||
modalX.show();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
92
zbiorka_app/static/js/ustawienia.js
Normal file
92
zbiorka_app/static/js/ustawienia.js
Normal file
@@ -0,0 +1,92 @@
|
||||
// static/js/ustawienia.js
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Formatowanie IBAN (PL)
|
||||
const iban = document.getElementById('numer_konta');
|
||||
if (iban) {
|
||||
iban.addEventListener('input', () => {
|
||||
const digits = iban.value.replace(/\D/g, '').slice(0, 26);
|
||||
const chunked = digits.replace(/(.{4})/g, '$1 ').trim();
|
||||
iban.value = chunked;
|
||||
});
|
||||
}
|
||||
|
||||
// Telefon BLIK 3-3-3
|
||||
const tel = document.getElementById('numer_telefonu_blik');
|
||||
if (tel) {
|
||||
tel.addEventListener('input', () => {
|
||||
const digits = tel.value.replace(/\D/g, '').slice(0, 9);
|
||||
const parts = [];
|
||||
if (digits.length > 0) parts.push(digits.substring(0, 3));
|
||||
if (digits.length > 3) parts.push(digits.substring(3, 6));
|
||||
if (digits.length > 6) parts.push(digits.substring(6, 9));
|
||||
tel.value = parts.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
// Biała lista IP/hostów
|
||||
const ta = document.getElementById('dozwolone_hosty_logowania');
|
||||
const count = document.getElementById('hostsCount');
|
||||
const addBtn = document.getElementById('btn-add-host');
|
||||
const addMyBtn = document.getElementById('btn-add-my-ip');
|
||||
const input = document.getElementById('host_input');
|
||||
const dedupeBtn = document.getElementById('btn-dedupe');
|
||||
|
||||
const parseList = (text) =>
|
||||
text
|
||||
.split(/[\r\n,;]+/) // \r?\n, przecinek, średnik
|
||||
.map(s => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const formatList = (arr) => arr.join('\n');
|
||||
|
||||
const dedupe = (arr) => {
|
||||
const seen = new Set();
|
||||
const out = [];
|
||||
for (const v of arr) {
|
||||
const k = v.toLowerCase();
|
||||
if (!seen.has(k)) { seen.add(k); out.push(v); }
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const updateCount = () => {
|
||||
if (!ta || !count) return;
|
||||
count.textContent = String(parseList(ta.value).length);
|
||||
};
|
||||
|
||||
const addEntry = (val) => {
|
||||
if (!ta || !val) return;
|
||||
const list = dedupe([...parseList(ta.value), val]);
|
||||
ta.value = formatList(list);
|
||||
updateCount();
|
||||
};
|
||||
|
||||
if (ta) {
|
||||
ta.addEventListener('input', updateCount);
|
||||
updateCount(); // inicjalne przeliczenie
|
||||
}
|
||||
|
||||
if (addBtn && input) {
|
||||
addBtn.addEventListener('click', () => {
|
||||
const val = (input.value || '').trim();
|
||||
if (!val) return;
|
||||
addEntry(val);
|
||||
input.value = '';
|
||||
input.focus();
|
||||
});
|
||||
}
|
||||
|
||||
if (addMyBtn) {
|
||||
addMyBtn.addEventListener('click', () => {
|
||||
const ip = addMyBtn.dataset.myIp || '';
|
||||
if (ip) addEntry(ip);
|
||||
});
|
||||
}
|
||||
|
||||
if (dedupeBtn && ta) {
|
||||
dedupeBtn.addEventListener('click', () => {
|
||||
ta.value = formatList(dedupe(parseList(ta.value)));
|
||||
updateCount();
|
||||
});
|
||||
}
|
||||
});
|
||||
27
zbiorka_app/static/js/walidacja_logowanie.js
Normal file
27
zbiorka_app/static/js/walidacja_logowanie.js
Normal file
@@ -0,0 +1,27 @@
|
||||
(function () {
|
||||
const form = document.querySelector('form.needs-validation');
|
||||
form.addEventListener('submit', function (e) {
|
||||
if (!form.checkValidity()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
}, false);
|
||||
})();
|
||||
|
||||
const pw = document.getElementById("haslo");
|
||||
const toggle = document.getElementById('togglePw');
|
||||
toggle.addEventListener('click', () => {
|
||||
const isText = pw.type === 'text';
|
||||
pw.type = isText ? "haslo" : 'text';
|
||||
toggle.textContent = isText ? 'Pokaż' : 'Ukryj';
|
||||
toggle.setAttribute('aria-pressed', (!isText).toString());
|
||||
pw.focus();
|
||||
});
|
||||
const caps = document.getElementById('capsWarning');
|
||||
function handleCaps(e) {
|
||||
const capsOn = e.getModifierState && e.getModifierState('CapsLock');
|
||||
caps.style.display = capsOn ? 'inline' : 'none';
|
||||
}
|
||||
pw.addEventListener('keyup', handleCaps);
|
||||
pw.addEventListener('keydown', handleCaps);
|
||||
37
zbiorka_app/static/js/walidacja_rejestracja.js
Normal file
37
zbiorka_app/static/js/walidacja_rejestracja.js
Normal file
@@ -0,0 +1,37 @@
|
||||
(function () {
|
||||
const form = document.querySelector('form.needs-validation');
|
||||
form.addEventListener('submit', function (e) {
|
||||
if (!form.checkValidity()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
const pw1 = document.getElementById("haslo");
|
||||
const pw2 = document.getElementById('password2');
|
||||
if (pw1.value !== pw2.value) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
pw2.setCustomValidity("Hasła muszą być identyczne.");
|
||||
pw2.reportValidity();
|
||||
} else {
|
||||
pw2.setCustomValidity("");
|
||||
}
|
||||
form.classList.add('was-validated');
|
||||
}, false);
|
||||
})();
|
||||
|
||||
const pw = document.getElementById("haslo");
|
||||
const toggle = document.getElementById('togglePw');
|
||||
toggle.addEventListener('click', () => {
|
||||
const isText = pw.type === 'text';
|
||||
pw.type = isText ? "haslo" : 'text';
|
||||
toggle.textContent = isText ? 'Pokaż' : 'Ukryj';
|
||||
pw.focus();
|
||||
});
|
||||
|
||||
const caps = document.getElementById('capsWarning');
|
||||
function handleCaps(e) {
|
||||
const capsOn = e.getModifierState && e.getModifierState('CapsLock');
|
||||
caps.style.display = capsOn ? 'inline' : 'none';
|
||||
}
|
||||
pw.addEventListener('keyup', handleCaps);
|
||||
pw.addEventListener('keydown', handleCaps);
|
||||
66
zbiorka_app/static/js/zbiorka.js
Normal file
66
zbiorka_app/static/js/zbiorka.js
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
(function () {
|
||||
// --- Formatowanie IBAN ---
|
||||
const ibanEl = document.getElementById('ibanInput') || document.getElementById('ibanDisplay');
|
||||
if (ibanEl) {
|
||||
const raw = (('value' in ibanEl ? ibanEl.value : ibanEl.textContent) || '')
|
||||
.toString().replace(/\s+/g, '').toUpperCase();
|
||||
const digits = raw.replace(/^PL/, '').replace(/\D/g, '').slice(0, 26);
|
||||
if (digits) {
|
||||
const pretty = 'PL ' + digits.replace(/(.{4})/g, '$1 ').trim();
|
||||
if ('value' in ibanEl) ibanEl.value = pretty; else ibanEl.textContent = pretty;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Formatowanie BLIK ---
|
||||
const blikEl = document.getElementById('blikInput') || document.getElementById('blikDisplay');
|
||||
if (blikEl) {
|
||||
const raw = (('value' in blikEl ? blikEl.value : blikEl.textContent) || '')
|
||||
.toString().replace(/\D/g, '').slice(0, 9);
|
||||
if (raw) {
|
||||
const pretty = [raw.slice(0, 3), raw.slice(3, 6), raw.slice(6, 9)]
|
||||
.filter(Boolean).join(' ');
|
||||
if ('value' in blikEl) blikEl.value = pretty; else blikEl.textContent = pretty;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Kopiowanie: wspiera data-copy-input i data-copy-target ---
|
||||
const buttons = document.querySelectorAll('[data-copy-input], [data-copy-target]');
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
const sel = btn.getAttribute('data-copy-input') || btn.getAttribute('data-copy-target');
|
||||
const el = sel ? document.querySelector(sel) : null;
|
||||
if (!el) return;
|
||||
|
||||
const textRaw = ('value' in el ? el.value : el.textContent || '')
|
||||
.toString().replace(/\u00A0/g, ' ').trim();
|
||||
|
||||
const copyWithFallback = async (text) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
} catch {
|
||||
// Fallback: tymczasowy textarea
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.top = '-1000px';
|
||||
ta.setAttribute('readonly', '');
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try { document.execCommand('copy'); } catch { /* ignore */ }
|
||||
document.body.removeChild(ta);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const original = btn.textContent;
|
||||
const ok = await copyWithFallback(textRaw);
|
||||
if (ok) {
|
||||
btn.textContent = 'Skopiowano!';
|
||||
btn.disabled = true;
|
||||
setTimeout(() => { btn.textContent = original; btn.disabled = false; }, 1200);
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user