⚙
Shutdown Follow Up Item
Total
0
Complete
0
Pending
0
⟳
Connecting...
P#1
0
P#2
0
P#3
0
UT
0
Total Items
0
Complete
0
In Progress / Pending
0
Cancel / N/A
0
Filter
Function: All
Static
Rotating
Status: All
Complete
Cancel
Pending / In Progress
SD Inspect: All
✕ Clear
+ Add Item
P#1 Follow Up Items
↓ CSV
↓ Excel
↓ All Sheets
P#2 Follow Up Items
(รวม list เพิ่มเติม)
↓ CSV
↓ Excel
↓ All Sheets
P#3 Follow Up Items
↓ CSV
↓ Excel
↓ All Sheets
UT Follow Up Items
↓ CSV
↓ Excel
↓ All Sheets
✕
‹
›
Add New Item
✕
EQ / Equipment
Function
Static
Rotating
SD Inspect
Response
Follow Up List
Action
Executed Date
Status
- In Progress -
Complete
Cancel
Keep monitor
Remark
แจ้ง MN
// ============================ // AUTH // ============================ function loginGoogle() { const provider = new firebase.auth.GoogleAuthProvider(); auth.signInWithRedirect(provider).catch(err => { const el = document.getElementById('login-error'); el.textContent = 'Login ไม่สำเร็จ: ' + err.message; el.style.display = 'block'; }); } // Handle redirect result when page loads auth.getRedirectResult().then(result => { if (result && result.user) { // Successfully signed in via redirect console.log('Redirect login success:', result.user.displayName); } }).catch(err => { if (err.code !== 'auth/no-auth-event') { const el = document.getElementById('login-error'); if (el) { el.textContent = 'Login ไม่สำเร็จ: ' + err.message; el.style.display = 'block'; } console.error('Redirect result error:', err); } }); function logoutUser() { if (confirm('ต้องการ Logout?')) auth.signOut(); } auth.onAuthStateChanged(user => { if (user) { // Hide login overlay document.getElementById('login-overlay').style.display = 'none'; // Show user info const pill = document.getElementById('user-pill'); pill.style.display = 'flex'; document.getElementById('user-name').textContent = user.displayName || user.email; if (user.photoURL) document.getElementById('user-avatar').src = user.photoURL; // Start syncing startFirestoreSync(); } else { document.getElementById('login-overlay').style.display = 'flex'; document.getElementById('user-pill').style.display = 'none'; setSyncStatus('disconnected'); } }); // ============================ // FIRESTORE SYNC // ============================ const TABS = ['p1','p2','p3','ut']; let unsubscribers = []; let firestoreReady = false; function setSyncStatus(state) { const icon = document.getElementById('sync-icon'); const text = document.getElementById('sync-text'); const pill = document.getElementById('sync-status'); if (state === 'live') { icon.textContent = '●'; text.textContent = 'Live'; pill.style.color = '#3fb950'; } else if (state === 'saving') { icon.textContent = '⟳'; text.textContent = 'Saving...'; pill.style.color = '#d29922'; } else if (state === 'disconnected') { icon.textContent = '○'; text.textContent = 'Offline'; pill.style.color = '#f85149'; } else { icon.textContent = '⟳'; text.textContent = 'Connecting...'; pill.style.color = '#58a6ff'; } } function startFirestoreSync() { setSyncStatus('connecting'); // Check if collections exist, if not seed with initData TABS.forEach(tab => { const colRef = db.collection(tab); // Listen for real-time updates const unsub = colRef.orderBy('item').onSnapshot(snapshot => { if (snapshot.empty && !firestoreReady) { // First time: seed data from initData() seedTabToFirestore(tab); } else { // Update local data from Firestore data[tab] = snapshot.docs.map(doc => ({ ...doc.data(), _docId: doc.id })); if (activeTab === tab) renderTable(tab); updateStats(tab); ['p1','p2','p3','ut'].forEach(t => { document.getElementById('badge-' + t).textContent = data[t].length; }); } setSyncStatus('live'); }, err => { console.error('Firestore error:', err); setSyncStatus('disconnected'); toast('Connection error: ' + err.message, 'error'); }); unsubscribers.push(unsub); }); firestoreReady = true; } async function seedTabToFirestore(tab) { // Load local data first initData(); const batch = db.batch(); data[tab].forEach(row => { const ref = db.collection(tab).doc(String(row.id)); const rowData = { ...row }; delete rowData.picture; // don't store base64 in Firestore batch.set(ref, rowData); }); await batch.commit(); toast(`โหลดข้อมูลเริ่มต้น ${tab.toUpperCase()} สำเร็จ`, 'success'); } // ============================ // OVERRIDE SAVE/DELETE TO USE FIRESTORE // ============================ // Override saveInlineEdit to write to Firestore const _origSaveInlineEdit = saveInlineEdit; saveInlineEdit = function(tab, id) { const row = data[tab].find(r => r.id === id); if (!row) return; const table = document.getElementById('table-' + tab); const trs = [...table.querySelectorAll('tbody tr')]; let editTr = null; for (const tr of trs) { if (tr.querySelector('.save-btn') && tr.querySelector('[data-field="eq"]')) { editTr = tr; break; } } if (editTr) { editTr.querySelectorAll('[data-field]').forEach(el => { row[el.dataset.field] = el.value; }); } editingRow = null; setSyncStatus('saving'); const docId = String(row._docId || row.id); const saveData = { ...row }; delete saveData.picture; delete saveData._docId; db.collection(tab).doc(docId).set(saveData) .then(() => { setSyncStatus('live'); toast('บันทึกสำเร็จ', 'success'); }) .catch(err => { setSyncStatus('disconnected'); toast('บันทึกไม่สำเร็จ: ' + err.message, 'error'); }); }; // Override saveModal to write to Firestore const _origSaveModal = saveModal; saveModal = function() { const eq = document.getElementById('f-eq').value.trim(); const followup = document.getElementById('f-followup').value.trim(); if (!eq && !followup) { toast('กรุณากรอก Equipment หรือ Follow Up List', 'error'); return; } const newId = nextId[activeTab]++; const row = { id: newId, item: (data[activeTab].length || 0) + 1, eq: document.getElementById('f-eq').value, function: document.getElementById('f-function').value, sd: document.getElementById('f-sd').value, followup: document.getElementById('f-followup').value, action: document.getElementById('f-action').value, execdate: document.getElementById('f-execdate').value, resp: document.getElementById('f-resp').value, remark: document.getElementById('f-remark').value, status: document.getElementById('f-status').value, mn: document.getElementById('f-mn').value, }; setSyncStatus('saving'); db.collection(activeTab).doc(String(newId)).set(row) .then(() => { setSyncStatus('live'); toast('เพิ่มข้อมูลสำเร็จ', 'success'); }) .catch(err => { setSyncStatus('disconnected'); toast('เพิ่มข้อมูลไม่สำเร็จ: ' + err.message, 'error'); }); closeModal(); }; // Override deleteRow to delete from Firestore const _origDeleteRow = deleteRow; deleteRow = function(tab, id) { if (!confirm('ต้องการลบแถวนี้?')) return; const row = data[tab].find(r => r.id === id); const docId = String(row?._docId || id); setSyncStatus('saving'); db.collection(tab).doc(docId).delete() .then(() => { setSyncStatus('live'); toast('ลบข้อมูลแล้ว', 'success'); }) .catch(err => { setSyncStatus('disconnected'); toast('ลบไม่สำเร็จ: ' + err.message, 'error'); }); }; // ============================ // INIT (local fallback while connecting) // ============================ initData(); populateSDFilter('p1'); ['p1','p2','p3','ut'].forEach(t => { document.getElementById('badge-' + t).textContent = data[t].length; }); renderTable('p1'); updateStats('p1');