ge-tool/components/NotificationManager.tsx

168 lines
6.4 KiB
TypeScript
Raw Normal View History

2025-12-10 06:41:43 +00:00
/**
* NotificationManager Component
* Handles browser notifications for submission completion
* Uses Supabase Realtime to listen for updates and localStorage to filter notifications
*/
import { useEffect } from 'react';
import { supabase } from '../utils/supabase';
interface NotificationManagerProps {
// No props needed - component manages its own state
}
const NotificationManager: React.FC<NotificationManagerProps> = () => {
useEffect(() => {
console.log('[NotificationManager] Component mounted');
// Request notification permission on mount
if ('Notification' in window && Notification.permission === 'default') {
Notification.requestPermission().then(permission => {
console.log('[NotificationManager] Notification permission:', permission);
});
} else {
console.log('[NotificationManager] Notification permission status:', Notification.permission);
}
console.log('[NotificationManager] Setting up Supabase subscription...');
// Subscribe to Supabase Realtime for submission updates
const channel = supabase
.channel('submissions-notifications')
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'submissions',
},
(payload) => {
console.log('[NotificationManager] Submission updated:', payload);
handleSubmissionUpdate(payload.new);
}
)
.subscribe((status) => {
console.log('[NotificationManager] Subscription status:', status);
});
console.log('[NotificationManager] Subscription created');
// Cleanup on unmount
return () => {
console.log('[NotificationManager] Cleaning up subscription');
supabase.removeChannel(channel);
};
}, []);
const handleSubmissionUpdate = (submission: any) => {
console.log('[NotificationManager] handleSubmissionUpdate called with:', submission);
// Only process completed or failed submissions
if (submission.status !== 'completed' && submission.status !== 'failed') {
console.log('[NotificationManager] Ignoring submission with status:', submission.status);
return;
}
// Check if this submission was initiated by current browser
const mySubmissions = getMySubmissions();
console.log('[NotificationManager] My submissions:', mySubmissions);
console.log('[NotificationManager] Current submission_id:', submission.submission_id);
if (!mySubmissions.includes(submission.submission_id)) {
console.log('[NotificationManager] Not our submission, ignoring');
return; // Not our submission, ignore
}
console.log('[NotificationManager] Showing notification...');
// Show notification
if ('Notification' in window && Notification.permission === 'granted') {
// Parse results to count errors
const results = submission.results || [];
const errorCount = results.filter((r: any) => r.status === 'error').length;
const totalCount = results.length;
const hasErrors = errorCount > 0;
// Determine title based on status
let title: string;
if (submission.status === 'failed' || hasErrors) {
title = `❌ Có ${errorCount} link bị lỗi`;
} else {
title = '✅ Cấp quyền TMS hoàn tất';
}
// Build body content with username and GE info
const input = submission.input || {};
const usernameList = input.username_list || [];
const geInput = input.ge_input || '';
// Format username (show first 3, then "...")
const usernameDisplay = usernameList.length > 3
? `${usernameList.slice(0, 3).join(', ')}...`
: usernameList.join(', ');
// Format GE input (show first 3 lines, then "...")
const geLines = geInput.split('\n').filter((line: string) => line.trim());
const geDisplay = geLines.length > 3
? `${geLines.slice(0, 3).join(', ')}...`
: geLines.join(', ');
const body = `${usernameDisplay}\n${geDisplay}`;
const notification = new Notification(title, {
body,
icon: '/push_noti.png',
tag: submission.submission_id, // Prevent duplicate notifications
});
// Focus window when notification is clicked
notification.onclick = () => {
window.focus();
notification.close();
};
// Auto close after 10 seconds
setTimeout(() => notification.close(), 10000);
console.log('[NotificationManager] Notification shown');
} else {
console.log('[NotificationManager] Notification permission not granted:', Notification.permission);
}
// Remove from localStorage after notification
removeMySubmission(submission.submission_id);
};
return null; // This component doesn't render anything
};
// Helper functions for localStorage management
const STORAGE_KEY = 'my_submissions';
export const addMySubmission = (submissionId: string) => {
const existing = getMySubmissions();
if (!existing.includes(submissionId)) {
existing.push(submissionId);
localStorage.setItem(STORAGE_KEY, JSON.stringify(existing));
console.log('[NotificationManager] Added submission to localStorage:', submissionId);
}
};
export const getMySubmissions = (): string[] => {
try {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
} catch {
return [];
}
};
export const removeMySubmission = (submissionId: string) => {
const existing = getMySubmissions();
const filtered = existing.filter(id => id !== submissionId);
localStorage.setItem(STORAGE_KEY, JSON.stringify(filtered));
console.log('[NotificationManager] Removed submission from localStorage:', submissionId);
};
export default NotificationManager;