diff --git a/shopping_app/static/css/style.css b/shopping_app/static/css/style.css index 3e5b75a..3d649c2 100644 --- a/shopping_app/static/css/style.css +++ b/shopping_app/static/css/style.css @@ -5714,3 +5714,328 @@ body:not(.sorting-active) .drag-handle { min-height: var(--ui-control-height) !important; border-radius: var(--ui-control-radius) !important; } + +/* Share hub redesign (mobile-first) */ +.share-hub { + border: 1px solid rgba(79, 142, 255, 0.18); + background: linear-gradient(180deg, rgba(11, 24, 43, 0.98), rgba(8, 17, 31, 0.96)) !important; +} + +.share-hub .card-body { + padding: 1rem; +} + +.share-hub__top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.9rem; + margin-bottom: 0.85rem; +} + +.share-hub__eyebrow, +.share-sheet__eyebrow { + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; + color: rgba(186, 210, 240, 0.62); + margin-bottom: 0.35rem; +} + +.share-hub__title { + font-size: 1.1rem; + font-weight: 700; +} + +.share-hub__status, +.share-sheet__section-head { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + align-items: center; +} + +.share-state-badge { + display: inline-flex; + align-items: center; + gap: 0.3rem; + min-height: 32px; + padding: 0.45rem 0.72rem; + font-size: 0.76rem; + font-weight: 600; + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.share-state-badge--public { + background: rgba(41, 209, 125, 0.16); + color: #dfffea; +} + +.share-state-badge--private { + background: rgba(255, 255, 255, 0.06); + color: #edf5ff; +} + +.share-state-badge--link { + background: rgba(79, 142, 255, 0.14); + color: #d7e7ff; +} + +.share-state-badge--people { + background: rgba(255, 255, 255, 0.08); + color: #edf5ff; +} + +.share-hub__note { + color: rgba(210, 224, 244, 0.74); + font-size: 0.92rem; + line-height: 1.45; +} + +.share-hub__linkbox { + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.035); + border-radius: 16px; + padding: 0.85rem 0.95rem; +} + +.share-hub__linklabel { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.07em; + color: rgba(186, 210, 240, 0.58); + margin-bottom: 0.3rem; +} + +.share-hub__linkvalue { + color: #f4f8ff; + font-size: 0.95rem; + line-height: 1.45; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.share-hub__actions { + display: grid; + grid-template-columns: 1fr; + gap: 0.65rem; +} + +.share-hub__primary, +.share-hub__secondary, +.share-hub__manage, +.share-sheet__toggle, +.share-sheet__sticky-actions .btn, +.share-sheet__linkstack .btn, + + +.share-hub__manage { + white-space: nowrap; +} + +.share-sheet { + height: auto !important; + max-height: min(90vh, 760px); + border-top-left-radius: 24px; + border-top-right-radius: 24px; + border: 1px solid rgba(255, 255, 255, 0.06); + background: linear-gradient(180deg, rgba(8, 18, 33, 0.995), rgba(6, 13, 24, 0.99)) !important; + box-shadow: 0 -24px 60px rgba(0, 0, 0, 0.42); +} + +.share-sheet__header { + align-items: flex-start; + padding: 0.85rem 1rem 0.6rem; +} + +.share-sheet__body { + padding: 0 1rem calc(1rem + env(safe-area-inset-bottom)); + overflow-y: auto; +} + +.share-sheet__grabber { + width: 52px; + height: 5px; + border-radius: 999px; + margin: 0 auto 0.8rem; + background: rgba(255, 255, 255, 0.22); +} + +.share-sheet__section { + border: 1px solid rgba(255, 255, 255, 0.07); + background: rgba(255, 255, 255, 0.035); + border-radius: 18px; + padding: 0.95rem; + margin-bottom: 0.9rem; +} + +.share-sheet__section-head { + justify-content: space-between; + margin-bottom: 0.7rem; + font-weight: 600; +} + +.share-sheet__linkstack, +.share-access-panel__input { + display: grid; + grid-template-columns: 1fr; + gap: 0.65rem; +} + +.share-access-panel .tokens { + min-height: 2rem; +} + +.share-access-panel .token { + background: rgba(255, 255, 255, 0.03); +} + +.share-sheet__sticky-actions { + position: sticky; + bottom: 0; + padding-top: 0.3rem; + background: linear-gradient(180deg, rgba(6, 13, 24, 0), rgba(6, 13, 24, 0.96) 28%); +} + +@media (min-width: 576px) { + .share-hub .card-body, + .share-sheet__header, + .share-sheet__body { + padding-left: 1.2rem; + padding-right: 1.2rem; + } + + .share-sheet__linkstack, + .share-access-panel__input { + grid-template-columns: 1fr auto; + align-items: center; + } +} + +@media (min-width: 768px) { + .share-hub .card-body { + padding: 1.15rem 1.2rem; + } + + .share-hub__actions { + grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); + } + + .share-sheet { + max-width: 760px; + margin: 0 auto; + left: 0; + right: 0; + } +} + + +/* v5.2 create-list unity + receipt collapse fix */ +.endpoint-main_page .create-list-input-group { + display: flex; + flex-wrap: nowrap !important; + align-items: stretch; + overflow: hidden; + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.12); + background: rgba(7, 17, 31, 0.9); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18); +} + +.endpoint-main_page .create-list-input-group > .create-list-title-input, +.endpoint-main_page .create-list-input-group > .form-control { + border: 0 !important; + border-right: 1px solid rgba(255, 255, 255, 0.08) !important; + border-radius: 0 !important; + background: transparent !important; + box-shadow: none !important; +} + +.endpoint-main_page .create-list-input-group > .create-list-title-input:focus, +.endpoint-main_page .create-list-input-group > .form-control:focus { + background: rgba(255, 255, 255, 0.02) !important; + box-shadow: none !important; +} + +.endpoint-main_page .create-list-input-group > .create-list-temp-toggle, +.endpoint-main_page .create-list-input-group > #tempToggle { + min-width: 9.5rem; + border: 0 !important; + border-radius: 0 !important; + background: rgba(255, 255, 255, 0.04) !important; + box-shadow: none !important; +} + +.endpoint-main_page .create-list-input-group > .create-list-temp-toggle.is-active, +.endpoint-main_page .create-list-input-group > #tempToggle.is-active { + background: rgba(41, 209, 125, 0.18) !important; +} + +.endpoint-main_page .create-list-temp-toggle__label { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 100%; +} + +.endpoint-main_page .create-list-input-group:focus-within { + border-color: rgba(41, 209, 125, 0.55); + box-shadow: 0 0 0 0.18rem rgba(41, 209, 125, 0.12), 0 10px 28px rgba(0, 0, 0, 0.18); +} + +.receipt-disclosure { + display: block; + padding: 0; + text-align: left; +} + +.receipt-disclosure, +.receipt-disclosure:hover, +.receipt-disclosure:focus, +.receipt-disclosure:active { + width: 100%; + appearance: none; + -webkit-appearance: none; +} + +.receipt-disclosure:focus-visible { + outline: none; +} + +.receipt-section--restoring { + transition: none !important; +} + +@media (max-width: 767.98px) { + .endpoint-main_page .create-list-input-group { + border-radius: 14px; + } + + .endpoint-main_page .create-list-input-group > .create-list-temp-toggle, + .endpoint-main_page .create-list-input-group > #tempToggle { + min-width: 8.25rem; + padding-left: .8rem; + padding-right: .8rem; + font-size: .9rem; + } +} + +@media (max-width: 575.98px) { + .endpoint-main_page .create-list-input-group > .create-list-title-input, + .endpoint-main_page .create-list-input-group > .form-control { + padding-left: .85rem; + padding-right: .7rem; + font-size: .95rem; + } + + .endpoint-main_page .create-list-input-group > .create-list-temp-toggle, + .endpoint-main_page .create-list-input-group > #tempToggle { + min-width: 7.6rem; + font-size: .84rem; + } + + .receipt-disclosure { + border-radius: 16px; + } +} diff --git a/shopping_app/static/js/access_users.js b/shopping_app/static/js/access_users.js index 2daf4d3..73937c3 100644 --- a/shopping_app/static/js/access_users.js +++ b/shopping_app/static/js/access_users.js @@ -19,6 +19,28 @@ tokensBox.appendChild(btn); } + function pluralizePeople(count) { + if (count === 1) return 'osoba'; + const mod10 = count % 10; + const mod100 = count % 100; + if (mod10 >= 2 && mod10 <= 4 && !(mod100 >= 12 && mod100 <= 14)) return 'osoby'; + return 'osób'; + } + + function syncAccessCount(box) { + if (!box) return; + const count = $$('.token', box).length; + const sheetBadge = document.getElementById('shareSheetPeopleBadge'); + const cardBadge = document.getElementById('sharePeopleBadge'); + + if (sheetBadge) sheetBadge.textContent = String(count); + + if (cardBadge) { + cardBadge.textContent = `👥 ${count} ${pluralizePeople(count)}`; + cardBadge.classList.toggle('d-none', count === 0); + } + } + function wantsJSON() { return { 'Accept': 'application/json', @@ -127,6 +149,7 @@ empty.textContent = 'Brak dodanych uprawnień.'; tokensBox.appendChild(empty); } + syncAccessCount(box); toast(`Odebrano dostęp: @${username}`, 'success'); } else { btn.disabled = false; btn.classList.remove('disabled'); @@ -151,6 +174,7 @@ if (res.data?.user) { appendToken(box, res.data.user); appended++; + syncAccessCount(box); } } else { failCount++; @@ -171,6 +195,7 @@ addBtn?.addEventListener('click', addUsers); input?.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); addUsers(); } }); + syncAccessCount(box); } document.addEventListener('DOMContentLoaded', () => { diff --git a/shopping_app/static/js/functions.js b/shopping_app/static/js/functions.js index a6f1063..e332151 100644 --- a/shopping_app/static/js/functions.js +++ b/shopping_app/static/js/functions.js @@ -243,27 +243,65 @@ function applyHidePurchased(isInit = false) { }); } +function formatShareUrlPreview(url) { + return String(url || '').replace(/^https?:\/\//, ''); +} + +function setVisibilityBadgeState(el, isPublic, publicLabel = '🌍 Publiczna', privateLabel = '🔒 Prywatna') { + if (!el) return; + el.classList.remove('share-state-badge--public', 'share-state-badge--private'); + el.classList.add(isPublic ? 'share-state-badge--public' : 'share-state-badge--private'); + el.textContent = isPublic ? publicLabel : privateLabel; +} + +function updateShareVisibilityUI(data) { + const shareUrl = data?.share_url || ''; + const isPublic = !!data?.is_public; + + const shareUrlInput = document.getElementById('shareUrlInput'); + const shareUrlPreview = document.getElementById('shareUrlPreview'); + const copyBtn = document.getElementById('copyBtn'); + const toggleBtn = document.getElementById('toggleVisibilityBtn'); + const mainNote = document.getElementById('shareVisibilityNote'); + const sheetNote = document.getElementById('shareSheetVisibilityNote'); + const mainOpenBtn = document.getElementById('openShareModeBtn'); + const sheetOpenBtn = document.getElementById('openShareModeBtnSheet'); + + if (shareUrlInput) shareUrlInput.value = shareUrl; + if (shareUrlPreview) shareUrlPreview.textContent = formatShareUrlPreview(shareUrl); + if (copyBtn) copyBtn.disabled = false; + if (mainOpenBtn) mainOpenBtn.href = shareUrl; + if (sheetOpenBtn) sheetOpenBtn.href = shareUrl; + + setVisibilityBadgeState(document.getElementById('shareVisibilityBadge'), isPublic); + setVisibilityBadgeState(document.getElementById('shareSheetVisibilityBadge'), isPublic, 'Publiczna', 'Prywatna'); + + if (mainNote) { + mainNote.textContent = isPublic + ? 'Lista działa publicznie i przez link udostępniania.' + : 'Lista działa przez link udostępniania i dla zaproszonych osób.'; + } + + if (sheetNote) { + sheetNote.textContent = isPublic + ? 'Lista jest widoczna publicznie i nadal działa przez link.' + : 'Lista nie jest publiczna, ale nadal działa przez link i dla zaproszonych osób.'; + } + + if (toggleBtn) { + toggleBtn.innerHTML = isPublic ? '🙈 Ustaw jako prywatną' : '🌍 Uczyń publiczną'; + } +} + function toggleVisibility(listId) { fetch('/toggle_visibility/' + listId, { method: 'POST' }) .then(response => response.json()) .then(data => { - const shareHeader = document.getElementById('share-header'); - const shareUrlSpan = document.getElementById('share-url'); - const copyBtn = document.getElementById('copyBtn'); - const toggleBtn = document.getElementById('toggleVisibilityBtn'); - - // URL zawsze widoczny i aktywny - shareUrlSpan.style.display = 'inline'; - shareUrlSpan.textContent = data.share_url; - copyBtn.disabled = false; - - if (data.is_public) { - shareHeader.textContent = '🔗 Udostępnij link (lista publiczna)'; - toggleBtn.innerHTML = '🙈 Ukryj listę'; - } else { - shareHeader.textContent = '🔗 Udostępnij link (widoczna tylko przez link / uprawnienia)'; - toggleBtn.innerHTML = '🐵 Uczyń publiczną'; - } + updateShareVisibilityUI(data); + showToast(data.is_public ? 'Lista jest teraz publiczna.' : 'Lista jest teraz prywatna.', 'success'); + }) + .catch(() => { + showToast('Nie udało się zmienić widoczności listy.', 'danger'); }); } @@ -438,26 +476,6 @@ function updateListSmoothly(newItems) { applyHidePurchased(); } - -document.addEventListener("DOMContentLoaded", function () { - const receiptSection = document.getElementById("receiptSection"); - const toggleBtn = document.querySelector('[data-bs-target="#receiptSection"]'); - - if (!receiptSection || !toggleBtn) return; - - if (localStorage.getItem("receiptSectionOpen") === "true") { - new bootstrap.Collapse(receiptSection, { toggle: true }); - } - - receiptSection.addEventListener('shown.bs.collapse', function () { - localStorage.setItem("receiptSectionOpen", "true"); - }); - - receiptSection.addEventListener('hidden.bs.collapse', function () { - localStorage.setItem("receiptSectionOpen", "false"); - }); -}); - document.addEventListener("DOMContentLoaded", function () { const toggle = document.getElementById('hidePurchasedToggle'); if (!toggle) return; diff --git a/shopping_app/static/js/receipt_section.js b/shopping_app/static/js/receipt_section.js index 73caa51..78876b1 100644 --- a/shopping_app/static/js/receipt_section.js +++ b/shopping_app/static/js/receipt_section.js @@ -1,55 +1,54 @@ document.addEventListener("DOMContentLoaded", function () { const receiptSection = document.getElementById("receiptSection"); - const toggleEl = document.querySelector('[data-bs-target="#receiptSection"]'); + const toggleEl = document.getElementById("toggleReceiptBtn"); - if (!receiptSection || !toggleEl) return; + if (!receiptSection || !toggleEl || typeof bootstrap === "undefined") return; + if (receiptSection.dataset.receiptInit === "1") return; + receiptSection.dataset.receiptInit = "1"; + const storageKey = receiptSection.dataset.receiptStorageKey || "receiptSectionOpen"; const collapse = bootstrap.Collapse.getOrCreateInstance(receiptSection, { toggle: false }); - - if (localStorage.getItem("receiptSectionOpen") === "true") { - collapse.show(); - } - const titleEl = toggleEl.querySelector(".receipt-disclosure__title"); - function updateUI() { - const isShown = receiptSection.classList.contains("show"); + function isShown() { + return receiptSection.classList.contains("show"); + } - toggleEl.classList.toggle("is-open", isShown); - toggleEl.setAttribute("aria-expanded", isShown ? "true" : "false"); + function persist(state) { + localStorage.setItem(storageKey, state ? "true" : "false"); + } + + function updateUI() { + const shown = isShown(); + toggleEl.classList.toggle("is-open", shown); + toggleEl.setAttribute("aria-expanded", shown ? "true" : "false"); if (titleEl) { - titleEl.textContent = isShown - ? "Ukryj sekcję paragonów" - : "Pokaż sekcję paragonów"; + titleEl.textContent = shown ? "Ukryj sekcję paragonów" : "Pokaż sekcję paragonów"; } } - function toggleSection() { + toggleEl.addEventListener("click", function () { collapse.toggle(); - } - - toggleEl.addEventListener("click", function (event) { - event.preventDefault(); - toggleSection(); - }); - - toggleEl.addEventListener("keydown", function (event) { - if (event.key === "Enter" || event.key === " ") { - event.preventDefault(); - toggleSection(); - } }); receiptSection.addEventListener("shown.bs.collapse", function () { - localStorage.setItem("receiptSectionOpen", "true"); + persist(true); updateUI(); }); receiptSection.addEventListener("hidden.bs.collapse", function () { - localStorage.setItem("receiptSectionOpen", "false"); + persist(false); updateUI(); }); + if (localStorage.getItem(storageKey) === "true") { + receiptSection.classList.add("receipt-section--restoring"); + collapse.show(); + requestAnimationFrame(function () { + receiptSection.classList.remove("receipt-section--restoring"); + }); + } + updateUI(); }); diff --git a/shopping_app/templates/list.html b/shopping_app/templates/list.html index c952a0e..e87f24c 100644 --- a/shopping_app/templates/list.html +++ b/shopping_app/templates/list.html @@ -30,38 +30,49 @@ - - ✅ Otwórz tryb odznaczania - +{% set share_url = url_for('shared_list', token=list.share_token, _external=True) %} +{% set permitted_count = permitted_users|length %} -
+
-
- - {% if list.is_public %}🔗 Udostępnij link (lista publiczna){% else %}🔗 Udostępnij link (widoczna przez link / - uprawnienia){% endif %} - - - {{ request.url_root }}share/{{ list.share_token }} - + -
- - + - - +
@@ -385,49 +396,87 @@ {% for username in all_usernames %}{% endfor %} - -