Review
+Das habe ich verstanden
+Wähle einen Kontakt im Diktieren-Tab aus, um Details anzuzeigen.
+Kontaktname
+Beschreibung
+Hinzufügen
+Neuen Kontakt erstellen
+Lege eine neue Person direkt in deinem CRM an.
+ +diff --git a/public/app.js b/public/app.js index f43cef5..d4ebfd6 100644 --- a/public/app.js +++ b/public/app.js @@ -4,6 +4,7 @@ const state = { contacts: [], recognition: null, listening: false, + selectedContactId: null, }; const elements = { @@ -29,8 +30,33 @@ const elements = { saveButton: document.querySelector("#saveButton"), discardButton: document.querySelector("#discardButton"), messageLog: document.querySelector("#messageLog"), + + // Tab Elements + tabButtons: document.querySelectorAll(".tab-btn"), + tabViews: document.querySelectorAll(".tab-view"), + tabBtnContactInfo: document.querySelector("#tabBtnContactInfo"), + + // Contact Info Elements + contactInfoPlaceholder: document.querySelector("#contact-info-placeholder"), + contactInfoContent: document.querySelector("#contact-info-content"), + contactDetailsAvatar: document.querySelector("#contactDetailsAvatar"), + contactDetailsName: document.querySelector("#contactDetailsName"), + contactDetailsDesc: document.querySelector("#contactDetailsDesc"), + contactDetailsEmail: document.querySelector("#contactDetailsEmail"), + contactDetailsPhone: document.querySelector("#contactDetailsPhone"), + contactDetailsAddress: document.querySelector("#contactDetailsAddress"), + + // Create Contact Elements + createContactForm: document.querySelector("#createContactForm"), + newContactFirstName: document.querySelector("#newContactFirstName"), + newContactLastName: document.querySelector("#newContactLastName"), + newContactEmail: document.querySelector("#newContactEmail"), + newContactPhone: document.querySelector("#newContactPhone"), + newContactAddress: document.querySelector("#newContactAddress"), + newContactDesc: document.querySelector("#newContactDesc"), }; +// Mode Buttons document.querySelectorAll("[data-mode]").forEach((button) => { button.addEventListener("click", () => { state.mode = button.dataset.mode; @@ -38,6 +64,20 @@ document.querySelectorAll("[data-mode]").forEach((button) => { }); }); +// Tab Switch Listeners +elements.tabButtons.forEach((btn) => { + btn.addEventListener("click", () => { + const tabName = btn.dataset.tab; + elements.tabButtons.forEach((b) => b.classList.toggle("is-active", b === btn)); + elements.tabViews.forEach((v) => v.classList.toggle("is-active", v.id === `view-${tabName}`)); + + if (tabName === "contact-info") { + loadSelectedContactDetails(); + } + }); +}); + +// Dictate Control Listeners elements.analyzeButton.addEventListener("click", analyzeInput); elements.clearButton.addEventListener("click", () => { elements.inputText.value = ""; @@ -53,12 +93,64 @@ elements.saveButton.addEventListener("click", saveEntry); elements.contactSearch.addEventListener("input", debounce(loadContacts, 240)); elements.voiceButton.addEventListener("click", toggleSpeech); +// Select Contact Listener +elements.contactSelect.addEventListener("change", () => { + const val = elements.contactSelect.value; + state.selectedContactId = val ? Number(val) : null; + if (state.selectedContactId) { + loadSelectedContactDetails(); + } else { + hideContactDetails(); + } +}); + +// Create Contact Form Listener +elements.createContactForm.addEventListener("submit", async (e) => { + e.preventDefault(); + + const contactData = { + first_name: elements.newContactFirstName.value.trim(), + last_name: elements.newContactLastName.value.trim(), + email: elements.newContactEmail.value.trim(), + phone: elements.newContactPhone.value.trim(), + address: elements.newContactAddress.value.trim(), + description: elements.newContactDesc.value.trim(), + }; + + try { + log("Erstelle neuen Kontakt in Monica..."); + const res = await api("/api/contacts/create", { + method: "POST", + body: JSON.stringify(contactData), + }); + + log(`Kontakt ${res.data.name} wurde erfolgreich erstellt!`); + elements.createContactForm.reset(); + + // Reload contacts and auto-select new one + await loadContacts(); + elements.contactSelect.value = res.data.id; + state.selectedContactId = res.data.id; + elements.contactSearch.value = res.data.name; + + // Switch to dictate and load details + elements.tabButtons[0].click(); + loadSelectedContactDetails(); + } catch (error) { + log(`Kontakt konnte nicht erstellt werden: ${error.message}`, "error"); + } +}); + await boot(); async function boot() { await checkHealth(); await loadContacts(); setupSpeechRecognition(); + + if (window.lucide) { + window.lucide.createIcons(); + } } async function checkHealth() { @@ -91,14 +183,57 @@ function renderContacts(selectedName = "") { const empty = new Option("Kein Kontakt ausgewählt", ""); elements.contactSelect.append(empty); + let selectedId = ""; for (const contact of state.contacts) { const option = new Option(contact.name, contact.id); option.dataset.name = contact.name; + if (selectedName && contact.name.toLowerCase().includes(selectedName.toLowerCase())) { option.selected = true; + selectedId = contact.id; } elements.contactSelect.append(option); } + + if (selectedId) { + state.selectedContactId = Number(selectedId); + } +} + +async function loadSelectedContactDetails() { + if (!state.selectedContactId) { + hideContactDetails(); + return; + } + + try { + const details = await api(`/api/contacts/${state.selectedContactId}`); + + elements.contactDetailsName.textContent = details.name; + elements.contactDetailsDesc.textContent = details.description || "Keine Beschreibung"; + elements.contactDetailsEmail.textContent = details.email || "-"; + elements.contactDetailsPhone.textContent = details.phone || "-"; + elements.contactDetailsAddress.textContent = details.address || "-"; + + const avatarLetter = details.first_name ? details.first_name.slice(0, 1).toUpperCase() : "?"; + elements.contactDetailsAvatar.textContent = avatarLetter; + + elements.contactInfoPlaceholder.hidden = true; + elements.contactInfoContent.removeAttribute("hidden"); + elements.contactInfoContent.style.display = "block"; + + if (window.lucide) { + window.lucide.createIcons(); + } + } catch (error) { + log(`Kontaktdetails konnten nicht geladen werden: ${error.message}`, "error"); + } +} + +function hideContactDetails() { + elements.contactInfoPlaceholder.hidden = false; + elements.contactInfoContent.setAttribute("hidden", "true"); + elements.contactInfoContent.style.display = "none"; } async function analyzeInput() { @@ -112,10 +247,14 @@ async function analyzeInput() { try { const analysis = await api("/api/analyze", { method: "POST", - body: JSON.stringify({ text, mode: state.mode }), + body: JSON.stringify({ + text, + mode: state.mode, + contacts: state.contacts + }), }); state.analysis = analysis; - renderAnalysis(analysis); + await renderAnalysis(analysis); log("Ich habe daraus einen Vorschlag gemacht. Bitte kurz prüfen."); } catch (error) { log(error.message, "error"); @@ -124,7 +263,7 @@ async function analyzeInput() { } } -function renderAnalysis(analysis) { +async function renderAnalysis(analysis) { elements.review.hidden = false; elements.typeSelect.value = analysis.type; elements.summaryInput.value = analysis.summary; @@ -135,10 +274,26 @@ function renderAnalysis(analysis) { elements.journalPost.value = analysis.journal?.post || ""; elements.reminders.innerHTML = ""; - const inferredContact = analysis.contact?.name || ""; - if (inferredContact) { - elements.contactSearch.value = inferredContact; - loadContacts().then(() => renderContacts(inferredContact)); + const inferredContact = analysis.contact; + if (inferredContact && inferredContact.id) { + let match = state.contacts.find(c => c.id === inferredContact.id); + if (!match) { + state.contacts.push({ + id: inferredContact.id, + name: inferredContact.name, + description: inferredContact.description || "" + }); + renderContacts(); + } + + elements.contactSearch.value = ""; + elements.contactSelect.value = inferredContact.id; + state.selectedContactId = inferredContact.id; + await loadSelectedContactDetails(); + } else { + elements.contactSelect.value = ""; + state.selectedContactId = null; + hideContactDetails(); } for (const reminder of analysis.reminders || []) { @@ -246,7 +401,7 @@ function setupSpeechRecognition() { recognition.addEventListener("end", () => { state.listening = false; elements.voiceButton.classList.remove("listening"); - elements.voiceButton.textContent = "Aufnehmen"; + elements.voiceButton.innerHTML = ' Aufnehmen'; }); recognition.addEventListener("error", (event) => { @@ -266,7 +421,7 @@ function toggleSpeech() { state.listening = true; elements.voiceButton.classList.add("listening"); - elements.voiceButton.textContent = "Stoppen"; + elements.voiceButton.innerHTML = ' Stoppen'; state.recognition.start(); } diff --git a/public/index.html b/public/index.html index 7fd4f39..9463d26 100644 --- a/public/index.html +++ b/public/index.html @@ -7,6 +7,12 @@ + + + + + +
@@ -32,102 +38,190 @@ + +Review
-Review
+Wähle einen Kontakt im Diktieren-Tab aus, um Details anzuzeigen.
+Beschreibung
+Hinzufügen
+Lege eine neue Person direkt in deinem CRM an.
+ +