diff --git a/api/handlers.go b/api/handlers.go index d0174e3..04a1e21 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -1365,6 +1365,12 @@ func (h *Handlers) orgToMap(org *lib.Entry) map[string]any { result["website"] = orgData.Website result["description"] = orgData.Description result["industry"] = orgData.Industry + result["phone"] = orgData.Phone + result["address"] = orgData.Address + result["city"] = orgData.City + result["state"] = orgData.State + result["founded"] = orgData.Founded + result["linkedin"] = orgData.LinkedIn result["contact_name"] = orgData.ContactName result["contact_email"] = orgData.ContactEmail } @@ -1641,6 +1647,96 @@ func (h *Handlers) UpdateOrg(w http.ResponseWriter, r *http.Request) { // --------------------------------------------------------------------------- // ListDealOrgs handles GET /api/projects/{projectID}/orgs — list orgs in this deal +// UpdateDealOrg handles PATCH /api/projects/{projectID}/orgs/{dealOrgID} +// Updates org details (on the organization entry) and deal-specific role (on the deal_org entry). +func (h *Handlers) UpdateDealOrg(w http.ResponseWriter, r *http.Request) { + actorID := UserIDFromContext(r.Context()) + projectID := chi.URLParam(r, "projectID") + dealOrgID := chi.URLParam(r, "dealOrgID") + + if err := lib.CheckAccessWrite(h.DB, actorID, projectID, ""); err != nil { + ErrorResponse(w, http.StatusForbidden, "access_denied", "Access denied") + return + } + + var body struct { + Name string `json:"name"` + Description string `json:"description"` + Role string `json:"role"` + Industry string `json:"industry"` + Website string `json:"website"` + Phone string `json:"phone"` + Address string `json:"address"` + City string `json:"city"` + State string `json:"state"` + Founded string `json:"founded"` + LinkedIn string `json:"linkedin"` + Logo string `json:"logo"` + Version int `json:"version"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + ErrorResponse(w, http.StatusBadRequest, "invalid_body", "Invalid request body") + return + } + if strings.TrimSpace(body.Name) == "" { + ErrorResponse(w, http.StatusBadRequest, "name_required", "Name is required") + return + } + + // Load the deal_org entry to get org_id and current data + dealOrgEntry, err := lib.EntryByID(h.DB, h.Cfg, dealOrgID) + if err != nil || dealOrgEntry == nil { + ErrorResponse(w, http.StatusNotFound, "not_found", "Deal org not found") + return + } + var dealOrgData lib.DealOrgData + json.Unmarshal([]byte(dealOrgEntry.DataText), &dealOrgData) + + // Update role on deal_org if changed + if body.Role != "" && body.Role != dealOrgData.Role { + dealOrgData.Role = body.Role + dealOrgJSON, _ := json.Marshal(dealOrgData) + dealOrgEntry.DataText = string(dealOrgJSON) + if err := lib.EntryWrite(h.DB, h.Cfg, actorID, dealOrgEntry); err != nil { + ErrorResponse(w, http.StatusInternalServerError, "internal", "Failed to update deal org role") + return + } + } + + // Load the organization entry and update its details + orgEntry, err := lib.EntryByID(h.DB, h.Cfg, dealOrgData.OrgID) + if err != nil || orgEntry == nil { + ErrorResponse(w, http.StatusNotFound, "not_found", "Organization not found") + return + } + var orgData lib.OrgData + json.Unmarshal([]byte(orgEntry.DataText), &orgData) + + orgData.Name = strings.TrimSpace(body.Name) + orgData.Description = body.Description + if body.Role != "" { orgData.Role = body.Role } + orgData.Industry = body.Industry + orgData.Website = body.Website + orgData.Phone = body.Phone + orgData.Address = body.Address + orgData.City = body.City + orgData.State = body.State + orgData.Founded = body.Founded + orgData.LinkedIn = body.LinkedIn + if body.Logo != "" { orgData.Logo = body.Logo } + + orgJSON, _ := json.Marshal(orgData) + orgEntry.DataText = string(orgJSON) + orgEntry.SummaryText = orgData.Name + + if err := lib.EntryWrite(h.DB, h.Cfg, actorID, orgEntry); err != nil { + ErrorResponse(w, http.StatusInternalServerError, "internal", "Failed to update organization") + return + } + + JSONResponse(w, http.StatusOK, map[string]any{"ok": true}) +} + func (h *Handlers) ListDealOrgs(w http.ResponseWriter, r *http.Request) { actorID := UserIDFromContext(r.Context()) projectID := chi.URLParam(r, "projectID") @@ -1711,6 +1807,12 @@ func (h *Handlers) ListDealOrgs(w http.ResponseWriter, r *http.Request) { dealOrgMap["org_description"] = orgDetails["description"] dealOrgMap["org_industry"] = orgDetails["industry"] dealOrgMap["org_website"] = orgDetails["website"] + dealOrgMap["org_phone"] = orgDetails["phone"] + dealOrgMap["org_address"] = orgDetails["address"] + dealOrgMap["org_city"] = orgDetails["city"] + dealOrgMap["org_state"] = orgDetails["state"] + dealOrgMap["org_founded"] = orgDetails["founded"] + dealOrgMap["org_linkedin"] = orgDetails["linkedin"] } } } diff --git a/api/routes.go b/api/routes.go index bbf2c60..a56d465 100644 --- a/api/routes.go +++ b/api/routes.go @@ -115,6 +115,7 @@ func NewRouter(db *lib.DB, cfg *lib.Config, store lib.ObjectStore, websiteFS fs. r.Post("/projects/{projectID}/orgs", h.CreateDealOrg) r.Delete("/projects/{projectID}/orgs/{dealOrgID}", h.DeleteDealOrg) r.Post("/projects/{projectID}/orgs/add", h.AddOrgToDeal) + r.Patch("/projects/{projectID}/orgs/{dealOrgID}", h.UpdateDealOrg) // Scrape (LLM-powered org lookup) r.Post("/scrape/org", h.ScrapeOrg) diff --git a/portal/templates/app/project.html b/portal/templates/app/project.html index 088b2e2..6d9e9db 100644 --- a/portal/templates/app/project.html +++ b/portal/templates/app/project.html @@ -73,6 +73,86 @@ + + +