Error: Application is not configured correctly.
`;
return;
}
const app = initializeApp(firebaseConfig);
db = getFirestore(app);
auth = getAuth(app);
onAuthStateChanged(auth, async (user) => {
if (user) {
userId = user.uid;
currentUserIdEl.textContent = userId;
await loadUserProfile();
listenForMessages(appId);
}
});
try {
if (initialAuthToken) {
await signInWithCustomToken(auth, initialAuthToken);
} else {
await signInAnonymously(auth);
}
} catch (error) {
console.error("Authentication failed:", error);
chatMessages.innerHTML = `
Error: Could not authenticate user.
`;
}
}
// --- CHAT LOGIC ---
function listenForMessages(appId) {
const messagesCol = collection(db, `/artifacts/${appId}/public/data/messages`);
const q = query(messagesCol); // In a real app, you'd add orderBy and limit
if (messagesUnsubscribe) messagesUnsubscribe(); // Unsubscribe from previous listener
messagesUnsubscribe = onSnapshot(q, (querySnapshot) => {
const allMessages = [];
querySnapshot.forEach((doc) => {
allMessages.push({ id: doc.id, ...doc.data() });
});
// Simple sort by timestamp in JS. Firestore's orderBy is better but requires indexing.
allMessages.sort((a, b) => a.timestamp?.toMillis() - b.timestamp?.toMillis());
renderMessages(allMessages);
});
}
async function sendMessage(e) {
e.preventDefault();
const text = messageInput.value.trim();
if (text === '' || !userId) return;
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-chat-app';
const messagesCol = collection(db, `/artifacts/${appId}/public/data/messages`);
try {
await addDoc(messagesCol, {
text: text,
userId: userId,
displayName: userDisplayName,
timestamp: serverTimestamp()
});
messageInput.value = '';
} catch (error) {
console.error("Error sending message: ", error);
}
}
function renderMessages(messages) {
chatMessages.innerHTML = '';
messages.forEach(msg => {
const isCurrentUser = msg.userId === userId;
const alignClass = isCurrentUser ? 'justify-end' : 'justify-start';
const bubbleColor = isCurrentUser ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-800';
const nameAlign = isCurrentUser ? 'text-right' : 'text-left';
const messageEl = document.createElement('div');
messageEl.className = `flex ${alignClass}`;
messageEl.innerHTML = `
${msg.displayName || 'Anonymous'}
`;
chatMessages.appendChild(messageEl);
});
// Auto-scroll to the bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// --- PROFILE & SETTINGS LOGIC ---
async function loadUserProfile() {
if (!userId) return;
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-chat-app';
const userProfileRef = doc(db, `/artifacts/${appId}/users/${userId}/profile/data`);
try {
const docSnap = await getDoc(userProfileRef);
if (docSnap.exists()) {
userDisplayName = docSnap.data().displayName || 'Anonymous';
}
} catch (error) {
console.error("Error loading user profile:", error);
}
displayNameInput.value = userDisplayName;
}
async function saveUserProfile() {
if (!userId) return;
const newName = displayNameInput.value.trim();
if (newName === '') return;
userDisplayName = newName;
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-chat-app';
const userProfileRef = doc(db, `/artifacts/${appId}/users/${userId}/profile/data`);
try {
await setDoc(userProfileRef, { displayName: newName }, { merge: true });
settingsFeedback.textContent = "Profile saved!";
setTimeout(() => settingsFeedback.textContent = "", 2000);
} catch (error) {
console.error("Error saving profile:", error);
settingsFeedback.textContent = "Error saving profile.";
}
}
// --- PDF GENERATION ---
async function generatePdf() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
doc.setFont("helvetica", "bold");
doc.setFontSize(22);
doc.setTextColor("#1e40af");
doc.text("Chat Transcript", doc.internal.pageSize.width / 2, 60, { align: 'center' });
doc.setFontSize(10);
doc.setTextColor("#64748b");
doc.text(`Generated on: ${new Date().toLocaleString()}`, doc.internal.pageSize.width / 2, 80, { align: 'center' });
const messagesSnapshot = await getDocs(query(collection(db, `/artifacts/${typeof __app_id !== 'undefined' ? __app_id : 'default-chat-app'}/public/data/messages`)));
let allMessages = [];
messagesSnapshot.forEach(doc => allMessages.push(doc.data()));
allMessages.sort((a, b) => a.timestamp?.toMillis() - b.timestamp?.toMillis());
const tableData = allMessages.map(msg => {
const timestamp = msg.timestamp ? msg.timestamp.toDate().toLocaleTimeString() : 'N/A';
return [timestamp, msg.displayName, msg.text];
});
doc.autoTable({
startY: 120,
head: [['Time', 'User', 'Message']],
body: tableData,
theme: 'grid',
headStyles: { fillColor: '#3b82f6' },
columnStyles: { 2: { cellWidth: 'auto' } }
});
doc.save(`Chat-Transcript_${new Date().toISOString().split('T')[0]}.pdf`);
}
// --- TAB NAVIGATION LOGIC ---
function switchTab(tabIndex) {
currentTabIndex = tabIndex;
tabsContainer.querySelectorAll('button').forEach((btn, index) => {
btn.classList.toggle('tab-active', index === tabIndex);
btn.classList.toggle('tab-inactive', index !== tabIndex);
});
tabContents.forEach((content, index) => {
content.classList.toggle('hidden', index !== tabIndex);
});
updateButtonStates();
}
function updateButtonStates() {
prevBtn.disabled = currentTabIndex === 0;
nextBtn.disabled = currentTabIndex === tabs.length - 1;
prevBtn.classList.toggle('opacity-50', prevBtn.disabled);
nextBtn.classList.toggle('opacity-50', nextBtn.disabled);
}
// --- EVENT LISTENERS ---
tabsContainer.querySelectorAll('button').forEach((btn, index) => {
btn.addEventListener('click', () => switchTab(index));
});
prevBtn.addEventListener('click', () => {
if (currentTabIndex > 0) switchTab(currentTabIndex - 1);
});
nextBtn.addEventListener('click', () => {
if (currentTabIndex < tabs.length - 1) switchTab(currentTabIndex + 1);
});
downloadPdfBtn.addEventListener('click', generatePdf);
messageForm.addEventListener('submit', sendMessage);
saveSettingsBtn.addEventListener('click', saveUserProfile);
// --- RUN INITIALIZATION ---
initializeFirebase();
updateButtonStates();
});