diff --git a/internal/db/migrate.go b/internal/db/migrate.go index 913f243..f11e554 100644 --- a/internal/db/migrate.go +++ b/internal/db/migrate.go @@ -22,6 +22,7 @@ func Migrate(db *sql.DB) error { createBuyerGroups, createFolderAccess, createFileComments, + createContactDeals, } for i, m := range migrations { @@ -220,6 +221,13 @@ CREATE TABLE IF NOT EXISTS folder_access ( PRIMARY KEY (folder_id, buyer_group) );` +const createContactDeals = ` +CREATE TABLE IF NOT EXISTS contact_deals ( + contact_id TEXT NOT NULL, + deal_id TEXT NOT NULL, + PRIMARY KEY (contact_id, deal_id) +);` + const createFileComments = ` CREATE TABLE IF NOT EXISTS file_comments ( id TEXT PRIMARY KEY, diff --git a/internal/handler/admin.go b/internal/handler/admin.go index 82a3118..2bfe93d 100644 --- a/internal/handler/admin.go +++ b/internal/handler/admin.go @@ -80,12 +80,23 @@ func (h *Handler) handleAdminContactForm(w http.ResponseWriter, r *http.Request) id := r.URL.Query().Get("id") var contact model.Contact + var linkedDealIDs []string if id != "" { h.db.QueryRow("SELECT id, full_name, email, phone, company, title, contact_type, tags, notes FROM contacts WHERE id = ? AND organization_id = ?", id, profile.OrganizationID).Scan( &contact.ID, &contact.FullName, &contact.Email, &contact.Phone, &contact.Company, &contact.Title, &contact.ContactType, &contact.Tags, &contact.Notes) + rows, _ := h.db.Query("SELECT deal_id FROM contact_deals WHERE contact_id = ?", id) + if rows != nil { + for rows.Next() { + var did string + rows.Scan(&did) + linkedDealIDs = append(linkedDealIDs, did) + } + rows.Close() + } } - templates.AdminContactForm(profile, &contact).Render(r.Context(), w) + deals := h.getDeals(profile) + templates.AdminContactForm(profile, &contact, deals, linkedDealIDs).Render(r.Context(), w) } func (h *Handler) handleAdminContactSave(w http.ResponseWriter, r *http.Request) { @@ -124,6 +135,13 @@ func (h *Handler) handleAdminContactSave(w http.ResponseWriter, r *http.Request) } } + // Save contact-deal associations + h.db.Exec("DELETE FROM contact_deals WHERE contact_id = ?", id) + dealIDs := r.Form["deal_ids"] + for _, did := range dealIDs { + h.db.Exec("INSERT INTO contact_deals (contact_id, deal_id) VALUES (?, ?)", id, did) + } + http.Redirect(w, r, "/admin/contacts", http.StatusSeeOther) } @@ -135,6 +153,7 @@ func (h *Handler) handleAdminContactDelete(w http.ResponseWriter, r *http.Reques return } + h.db.Exec("DELETE FROM contact_deals WHERE contact_id = ?", id) h.db.Exec("DELETE FROM contacts WHERE id = ? AND organization_id = ?", id, profile.OrganizationID) http.Redirect(w, r, "/admin/contacts", http.StatusSeeOther) } diff --git a/internal/handler/contacts.go b/internal/handler/contacts.go index df8aeed..7500077 100644 --- a/internal/handler/contacts.go +++ b/internal/handler/contacts.go @@ -1,6 +1,7 @@ package handler import ( + "database/sql" "net/http" "dealroom/internal/model" @@ -12,7 +13,15 @@ func (h *Handler) handleContacts(w http.ResponseWriter, r *http.Request) { dealID := r.URL.Query().Get("deal_id") deals := h.getDeals(profile) - rows, err := h.db.Query("SELECT id, full_name, email, phone, company, title, contact_type, tags FROM contacts WHERE organization_id = ? ORDER BY full_name", profile.OrganizationID) + var rows *sql.Rows + var err error + if dealID != "" { + rows, err = h.db.Query(`SELECT c.id, c.full_name, c.email, c.phone, c.company, c.title, c.contact_type, c.tags + FROM contacts c JOIN contact_deals cd ON c.id = cd.contact_id + WHERE c.organization_id = ? AND cd.deal_id = ? ORDER BY c.full_name`, profile.OrganizationID, dealID) + } else { + rows, err = h.db.Query("SELECT id, full_name, email, phone, company, title, contact_type, tags FROM contacts WHERE organization_id = ? ORDER BY full_name", profile.OrganizationID) + } if err != nil { http.Error(w, "Error loading contacts", 500) return @@ -26,18 +35,16 @@ func (h *Handler) handleContacts(w http.ResponseWriter, r *http.Request) { contacts = append(contacts, c) } - // Filter contacts by deal's target company if deal_id is set - if dealID != "" { - var targetCompany string - h.db.QueryRow("SELECT target_company FROM deals WHERE id = ?", dealID).Scan(&targetCompany) - if targetCompany != "" { - var filtered []*model.Contact - for _, c := range contacts { - if c.Company == targetCompany { - filtered = append(filtered, c) - } + // Load deal names for each contact + for _, c := range contacts { + drows, _ := h.db.Query(`SELECT d.name FROM deals d JOIN contact_deals cd ON d.id = cd.deal_id WHERE cd.contact_id = ?`, c.ID) + if drows != nil { + for drows.Next() { + var name string + drows.Scan(&name) + c.DealNames = append(c.DealNames, name) } - contacts = filtered + drows.Close() } } diff --git a/internal/model/models.go b/internal/model/models.go index a06db6c..657bab8 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -119,6 +119,8 @@ type Contact struct { Notes string LastActivityAt *time.Time CreatedAt time.Time + // Computed + DealNames []string } type DealActivity struct { diff --git a/templates/admin.templ b/templates/admin.templ index 50df42b..f90696a 100644 --- a/templates/admin.templ +++ b/templates/admin.templ @@ -132,7 +132,7 @@ templ AdminContacts(profile *model.Profile, contacts []*model.Contact, filter st // --- Contact Form --- -templ AdminContactForm(profile *model.Profile, contact *model.Contact) { +templ AdminContactForm(profile *model.Profile, contact *model.Contact, deals []*model.Deal, linkedDealIDs []string) { @Layout(profile, "admin") {
@adminBreadcrumbSub("Contacts", "/admin/contacts", formTitle("Contact", contact.ID)) @@ -150,6 +150,21 @@ templ AdminContactForm(profile *model.Profile, contact *model.Contact) { }) @formField("tags", "Tags", "text", contact.Tags, false) @formTextarea("notes", "Notes", contact.Notes) +
+ +
+ for _, d := range deals { + + } +
+
@formActions("/admin/contacts")
@@ -436,6 +451,15 @@ type SelectOption struct { Label string } +func dealLinked(dealID string, linked []string) bool { + for _, id := range linked { + if id == dealID { + return true + } + } + return false +} + func formTitle(entity string, id string) string { if id == "" { return "New " + entity diff --git a/templates/contacts.templ b/templates/contacts.templ index e5f7386..e6d19ae 100644 --- a/templates/contacts.templ +++ b/templates/contacts.templ @@ -34,6 +34,7 @@ templ ContactsPage(profile *model.Profile, contacts []*model.Contact, deals []*m Company Email Type + Deals Tags @@ -54,6 +55,13 @@ templ ContactsPage(profile *model.Profile, contacts []*model.Contact, deals []*m { contact.Company } { contact.Email } @ContactTypeBadge(contact.ContactType) + +
+ for _, dn := range contact.DealNames { + { dn } + } +
+
for _, tag := range splitTags(contact.Tags) {