feat: Replace alert() with flash notifications and add bell notification
- Replace all alert() calls with showNotification() for consistent UX - Add UserNotification creation when admin proposes changes - User sees notification in bell icon with link to review changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
193d5ad8e3
commit
6370abb24f
@ -15,7 +15,7 @@ from flask_login import login_required, current_user
|
||||
from . import bp
|
||||
from database import (
|
||||
SessionLocal, MembershipApplication, CompanyDataRequest,
|
||||
Company, Category, User
|
||||
Company, Category, User, UserNotification
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -438,6 +438,18 @@ def admin_membership_propose_changes(app_id):
|
||||
application.registry_data = registry_data
|
||||
application.registry_source = registry_data.get('source', 'KRS')
|
||||
|
||||
# Create notification for user
|
||||
notification = UserNotification(
|
||||
user_id=application.user_id,
|
||||
title='Propozycja zmian w deklaracji',
|
||||
message=f'Administrator zaproponował aktualizację danych firmy "{application.company_name}" na podstawie rejestru {registry_data.get("source", "KRS")}. Przejrzyj i zaakceptuj lub odrzuć zmiany.',
|
||||
notification_type='alert',
|
||||
related_type='membership_application',
|
||||
related_id=app_id,
|
||||
action_url=f'/membership/review-changes/{app_id}'
|
||||
)
|
||||
db.add(notification)
|
||||
|
||||
db.commit()
|
||||
|
||||
logger.info(
|
||||
|
||||
@ -898,6 +898,33 @@ const appNip = '{{ application.nip or "" }}';
|
||||
const csrfToken = '{{ csrf_token() }}';
|
||||
let registryData = null;
|
||||
|
||||
// Ładne powiadomienia zamiast alert()
|
||||
function showNotification(message, type = 'info') {
|
||||
// type: 'success', 'error', 'warning', 'info'
|
||||
let container = document.querySelector('.flash-messages');
|
||||
if (!container) {
|
||||
container = document.createElement('div');
|
||||
container.className = 'flash-messages';
|
||||
container.setAttribute('role', 'alert');
|
||||
container.setAttribute('aria-live', 'polite');
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
const flash = document.createElement('div');
|
||||
flash.className = `flash flash-${type}`;
|
||||
flash.innerHTML = `
|
||||
<span>${message}</span>
|
||||
<button class="flash-close" onclick="this.parentElement.remove()" aria-label="Close">×</button>
|
||||
`;
|
||||
container.appendChild(flash);
|
||||
|
||||
// Auto-dismiss after 5 seconds
|
||||
setTimeout(() => {
|
||||
flash.style.animation = 'slideOut 0.3s ease-out';
|
||||
setTimeout(() => flash.remove(), 300);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
function setLookupStatus(message, type) {
|
||||
const statusEl = document.getElementById('lookupStatus');
|
||||
if (statusEl) {
|
||||
@ -910,7 +937,7 @@ function setLookupStatus(message, type) {
|
||||
// Pobieranie danych z rejestru
|
||||
async function lookupRegistry() {
|
||||
if (!appNip) {
|
||||
alert('Brak NIP w deklaracji');
|
||||
showNotification('Brak NIP w deklaracji', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1021,7 +1048,7 @@ async function lookupRegistry() {
|
||||
|
||||
function openProposeModal() {
|
||||
if (!registryData) {
|
||||
alert('Brak danych do zaproponowania');
|
||||
showNotification('Najpierw pobierz dane z rejestru', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1062,7 +1089,7 @@ function openProposeModal() {
|
||||
|
||||
async function proposeChanges() {
|
||||
if (!registryData) {
|
||||
alert('Brak danych do zaproponowania');
|
||||
showNotification('Najpierw pobierz dane z rejestru', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1080,13 +1107,14 @@ async function proposeChanges() {
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
alert('Zmiany zostały zaproponowane użytkownikowi. Status deklaracji zmieniono na "Oczekuje na akceptację użytkownika".');
|
||||
location.reload();
|
||||
showNotification('Propozycja zmian została wysłana do użytkownika. Oczekuje na akceptację.', 'success');
|
||||
closeModal('proposeModal');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
alert(result.error || 'Błąd');
|
||||
showNotification(result.error || 'Wystąpił błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Błąd połączenia');
|
||||
showNotification('Błąd połączenia z serwerem', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1114,12 +1142,13 @@ async function startReview() {
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
showNotification('Rozpoczęto rozpatrywanie deklaracji', 'success');
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
} else {
|
||||
alert(result.error || 'Błąd');
|
||||
showNotification(result.error || 'Wystąpił błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Błąd połączenia');
|
||||
showNotification('Błąd połączenia z serwerem', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1135,20 +1164,21 @@ async function approve() {
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
alert(`Zatwierdzono! Nr członkowski: ${result.member_number}`);
|
||||
location.reload();
|
||||
showNotification(`Deklaracja zatwierdzona! Nr członkowski: ${result.member_number}`, 'success');
|
||||
closeModal('approveModal');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
alert(result.error || 'Błąd');
|
||||
showNotification(result.error || 'Wystąpił błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Błąd połączenia');
|
||||
showNotification('Błąd połączenia z serwerem', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function reject() {
|
||||
const comment = document.getElementById('rejectComment').value.trim();
|
||||
if (!comment) {
|
||||
alert('Podaj powód odrzucenia');
|
||||
showNotification('Podaj powód odrzucenia', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1160,19 +1190,21 @@ async function reject() {
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
showNotification('Deklaracja została odrzucona', 'info');
|
||||
closeModal('rejectModal');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
alert(result.error || 'Błąd');
|
||||
showNotification(result.error || 'Wystąpił błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Błąd połączenia');
|
||||
showNotification('Błąd połączenia z serwerem', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function requestChanges() {
|
||||
const comment = document.getElementById('changesComment').value.trim();
|
||||
if (!comment) {
|
||||
alert('Opisz wymagane poprawki');
|
||||
showNotification('Opisz wymagane poprawki', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1184,12 +1216,14 @@ async function requestChanges() {
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
showNotification('Prośba o poprawki została wysłana', 'success');
|
||||
closeModal('changesModal');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
alert(result.error || 'Błąd');
|
||||
showNotification(result.error || 'Wystąpił błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Błąd połączenia');
|
||||
showNotification('Błąd połączenia z serwerem', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user