feat: edit org from deal — PATCH /orgs/{dealOrgId} updates org details + role; full field set in ListDealOrgs response
This commit is contained in:
parent
df096438e3
commit
83a3a43bff
102
api/handlers.go
102
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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,86 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Org Modal -->
|
||||
<div id="editOrgModal" class="hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4">
|
||||
<div class="bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<div class="flex items-center justify-between px-6 pt-5 pb-4 border-b border-white/[0.08]">
|
||||
<h2 class="text-lg font-semibold text-white">Edit Organization</h2>
|
||||
<button onclick="closeEditOrgModal()" class="text-[#b0bec5] hover:text-white text-2xl leading-none">×</button>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<input type="hidden" id="editOrgDealOrgId">
|
||||
<input type="hidden" id="editOrgVersion">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="col-span-2 flex items-start gap-4">
|
||||
<div id="editOrgLogoWrap" class="hidden shrink-0 w-16 h-16 rounded-lg border border-white/[0.08] overflow-hidden bg-white flex items-center justify-center">
|
||||
<img id="editOrgLogoImg" src="" class="max-w-full max-h-full object-contain">
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Name <span class="text-red-400">*</span></label>
|
||||
<input type="text" id="editOrgName" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Description</label>
|
||||
<textarea id="editOrgDesc" rows="3" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] resize-none"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Role in deal</label>
|
||||
<select id="editOrgRole" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
<option value="seller">Seller</option>
|
||||
<option value="buyer">Buyer</option>
|
||||
<option value="ib">Investment Bank</option>
|
||||
<option value="advisor">Advisor</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Industry</label>
|
||||
<input type="text" id="editOrgIndustry" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Website</label>
|
||||
<input type="text" id="editOrgWebsite" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Phone</label>
|
||||
<input type="text" id="editOrgPhone" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">LinkedIn</label>
|
||||
<input type="text" id="editOrgLinkedIn" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Founded</label>
|
||||
<input type="text" id="editOrgFounded" placeholder="e.g. 2010" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Address</label>
|
||||
<input type="text" id="editOrgAddress" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">City</label>
|
||||
<input type="text" id="editOrgCity" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">State</label>
|
||||
<input type="text" id="editOrgState" class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]">
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-[#b0bec5] mb-1">Logo URL</label>
|
||||
<input type="text" id="editOrgLogo" placeholder="https://..." class="w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]"
|
||||
oninput="previewEditOrgLogo()">
|
||||
</div>
|
||||
</div>
|
||||
<div id="editOrgError" class="hidden mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm"></div>
|
||||
</div>
|
||||
<div class="flex gap-3 px-6 pb-6">
|
||||
<button onclick="closeEditOrgModal()" class="flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition">Cancel</button>
|
||||
<button id="editOrgSaveBtn" onclick="saveEditOrg()" class="flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Template Picker Modal -->
|
||||
<div id="templateModal" style="display:none" class="fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4">
|
||||
<div class="bg-[#0d1f3c] rounded-xl w-full max-w-lg border border-white/[0.08]">
|
||||
|
|
@ -1201,6 +1281,13 @@
|
|||
_dealOrgsMap = {};
|
||||
if (Array.isArray(orgs)) orgs.forEach(o => {
|
||||
o._raw = { org_id: o.org_id, role: o.role, domain_lock: o.domain_lock, permissions: o.permissions, members: o.members };
|
||||
// Store org detail fields for edit modal
|
||||
o.org_details = {
|
||||
name: o.org_name || '', description: o.org_description || '', role: o.role || '',
|
||||
industry: o.org_industry || '', website: o.org_website || '', phone: o.org_phone || '',
|
||||
address: o.org_address || '', city: o.org_city || '', state: o.org_state || '',
|
||||
founded: o.org_founded || '', linkedin: o.org_linkedin || '', logo: o.org_logo || '',
|
||||
};
|
||||
_dealOrgsMap[o.deal_org_id] = o;
|
||||
});
|
||||
const list = document.getElementById('orgList');
|
||||
|
|
@ -1247,6 +1334,8 @@
|
|||
<button onclick="toggleOrgMenu('${o.deal_org_id}')"
|
||||
class="px-2 py-1.5 rounded-lg text-sm font-medium transition" style="background:var(--ds-hv);color:var(--ds-tx2)" title="More options">⋯</button>
|
||||
<div id="orgMenuDrop_${o.deal_org_id}" class="hidden absolute right-0 top-full mt-1 rounded-lg shadow-xl z-30 py-1 min-w-[160px]" style="background:var(--ds-card);border:1px solid var(--ds-bd)">
|
||||
<button onclick="openEditOrgModal('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')"
|
||||
class="w-full text-left px-4 py-2 text-sm transition hover:opacity-80" style="color:var(--ds-tx)">Edit details</button>
|
||||
<button onclick="openPermModalById('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')"
|
||||
class="w-full text-left px-4 py-2 text-sm transition hover:opacity-80" style="color:var(--ds-tx)">Permissions</button>
|
||||
<div style="height:1px;background:var(--ds-bd);margin:4px 0"></div>
|
||||
|
|
@ -1609,11 +1698,13 @@
|
|||
}
|
||||
|
||||
function renderPeople() {
|
||||
try {
|
||||
const people = scrapedData?.people || [];
|
||||
const list = document.getElementById('peopleList');
|
||||
const none = document.getElementById('noPeople');
|
||||
if (people.length === 0) { list.innerHTML = ''; none.classList.remove('hidden'); return; }
|
||||
none.classList.add('hidden');
|
||||
if (!list) { console.error('peopleList element not found'); return; }
|
||||
if (people.length === 0) { list.innerHTML = ''; if(none) none.classList.remove('hidden'); return; }
|
||||
if(none) none.classList.add('hidden');
|
||||
list.innerHTML = people.map((p, i) => {
|
||||
const hasDetail = p.name && p.title;
|
||||
return `
|
||||
|
|
@ -1632,8 +1723,10 @@
|
|||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
document.getElementById('selectAllPeople').checked = true;
|
||||
const sa = document.getElementById('selectAllPeople');
|
||||
if(sa) sa.checked = true;
|
||||
updateSelectedCount();
|
||||
} catch(err) { alert('renderPeople error: ' + err.message + '\n' + err.stack); }
|
||||
}
|
||||
|
||||
function updatePerson(el) {
|
||||
|
|
@ -1645,24 +1738,29 @@
|
|||
}
|
||||
|
||||
function addManualPerson() {
|
||||
const name = document.getElementById('manualName').value.trim();
|
||||
const email = document.getElementById('manualEmail').value.trim();
|
||||
const title = document.getElementById('manualTitle').value.trim();
|
||||
if (!name && !email) {
|
||||
const nameEl = document.getElementById('manualName');
|
||||
nameEl.classList.add('field-error');
|
||||
nameEl.placeholder = 'Enter a name or email';
|
||||
setTimeout(() => { nameEl.classList.remove('field-error'); nameEl.placeholder = 'Name'; }, 2500);
|
||||
nameEl.focus();
|
||||
return;
|
||||
try {
|
||||
const name = document.getElementById('manualName').value.trim();
|
||||
const email = document.getElementById('manualEmail').value.trim();
|
||||
const title = document.getElementById('manualTitle').value.trim();
|
||||
if (!name && !email) {
|
||||
const nameEl = document.getElementById('manualName');
|
||||
nameEl.classList.add('field-error');
|
||||
nameEl.placeholder = 'Enter a name or email';
|
||||
setTimeout(() => { nameEl.classList.remove('field-error'); nameEl.placeholder = 'Name'; }, 2500);
|
||||
nameEl.focus();
|
||||
return;
|
||||
}
|
||||
if (!scrapedData) scrapedData = { domain: '', people: [] };
|
||||
if (!scrapedData.people) scrapedData.people = [];
|
||||
scrapedData.people.push({ name: name || email, email, title, phone: '', photo: '', bio: '', linkedin: '' });
|
||||
document.getElementById('manualName').value = '';
|
||||
document.getElementById('manualEmail').value = '';
|
||||
document.getElementById('manualTitle').value = '';
|
||||
renderPeople();
|
||||
document.getElementById('manualName').focus();
|
||||
} catch(err) {
|
||||
alert('addManualPerson error: ' + err.message + '\n' + err.stack);
|
||||
}
|
||||
if (!scrapedData) scrapedData = { domain: '', people: [] };
|
||||
scrapedData.people.push({ name: name || email, email, title, phone: '', photo: '', bio: '', linkedin: '' });
|
||||
document.getElementById('manualName').value = '';
|
||||
document.getElementById('manualEmail').value = '';
|
||||
document.getElementById('manualTitle').value = '';
|
||||
renderPeople();
|
||||
document.getElementById('manualName').focus();
|
||||
}
|
||||
|
||||
function toggleAllPeople(checked) {
|
||||
|
|
@ -1867,6 +1965,122 @@
|
|||
});
|
||||
|
||||
// ---- Permissions Modal ----
|
||||
// ---- Edit Org Modal ----
|
||||
function openEditOrgModal(dealOrgId) {
|
||||
const o = _dealOrgsMap[dealOrgId];
|
||||
if (!o) return;
|
||||
const d = o.org_details || {};
|
||||
|
||||
document.getElementById('editOrgDealOrgId').value = dealOrgId;
|
||||
document.getElementById('editOrgVersion').value = o.version || 1;
|
||||
document.getElementById('editOrgName').value = d.name || o.org_name || '';
|
||||
document.getElementById('editOrgDesc').value = d.description || '';
|
||||
document.getElementById('editOrgRole').value = d.role || o.role || '';
|
||||
document.getElementById('editOrgIndustry').value = d.industry || '';
|
||||
document.getElementById('editOrgWebsite').value = d.website || '';
|
||||
document.getElementById('editOrgPhone').value = d.phone || '';
|
||||
document.getElementById('editOrgLinkedIn').value = d.linkedin || '';
|
||||
document.getElementById('editOrgFounded').value = d.founded || '';
|
||||
document.getElementById('editOrgAddress').value = d.address || '';
|
||||
document.getElementById('editOrgCity').value = d.city || '';
|
||||
document.getElementById('editOrgState').value = d.state || '';
|
||||
document.getElementById('editOrgLogo').value = d.logo || '';
|
||||
document.getElementById('editOrgError').classList.add('hidden');
|
||||
|
||||
const logo = d.logo || '';
|
||||
if (logo) {
|
||||
document.getElementById('editOrgLogoImg').src = logo;
|
||||
document.getElementById('editOrgLogoWrap').classList.remove('hidden');
|
||||
} else {
|
||||
document.getElementById('editOrgLogoWrap').classList.add('hidden');
|
||||
}
|
||||
|
||||
document.getElementById('editOrgModal').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeEditOrgModal() {
|
||||
document.getElementById('editOrgModal').classList.add('hidden');
|
||||
}
|
||||
|
||||
function previewEditOrgLogo() {
|
||||
const url = document.getElementById('editOrgLogo').value.trim();
|
||||
const img = document.getElementById('editOrgLogoImg');
|
||||
const wrap = document.getElementById('editOrgLogoWrap');
|
||||
if (url) {
|
||||
img.src = url;
|
||||
wrap.classList.remove('hidden');
|
||||
img.onerror = () => wrap.classList.add('hidden');
|
||||
} else {
|
||||
wrap.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function saveEditOrg() {
|
||||
const dealOrgId = document.getElementById('editOrgDealOrgId').value;
|
||||
const version = parseInt(document.getElementById('editOrgVersion').value) || 1;
|
||||
const name = document.getElementById('editOrgName').value.trim();
|
||||
const errEl = document.getElementById('editOrgError');
|
||||
errEl.classList.add('hidden');
|
||||
|
||||
if (!name) {
|
||||
document.getElementById('editOrgName').classList.add('field-error');
|
||||
errEl.textContent = 'Name is required.';
|
||||
errEl.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
const o = _dealOrgsMap[dealOrgId];
|
||||
if (!o) return;
|
||||
|
||||
const updatedRaw = {
|
||||
...(o._raw || {}),
|
||||
name,
|
||||
description: document.getElementById('editOrgDesc').value.trim(),
|
||||
role: document.getElementById('editOrgRole').value,
|
||||
industry: document.getElementById('editOrgIndustry').value.trim(),
|
||||
website: document.getElementById('editOrgWebsite').value.trim(),
|
||||
phone: document.getElementById('editOrgPhone').value.trim(),
|
||||
linkedin: document.getElementById('editOrgLinkedIn').value.trim(),
|
||||
founded: document.getElementById('editOrgFounded').value.trim(),
|
||||
address: document.getElementById('editOrgAddress').value.trim(),
|
||||
city: document.getElementById('editOrgCity').value.trim(),
|
||||
state: document.getElementById('editOrgState').value.trim(),
|
||||
logo: document.getElementById('editOrgLogo').value.trim(),
|
||||
};
|
||||
|
||||
const btn = document.getElementById('editOrgSaveBtn');
|
||||
btn.disabled = true; btn.textContent = 'Saving...';
|
||||
|
||||
try {
|
||||
const res = await fetchAPI('/api/projects/' + projectID + '/orgs/' + dealOrgId, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
name: updatedRaw.name,
|
||||
description: updatedRaw.description,
|
||||
role: updatedRaw.role,
|
||||
industry: updatedRaw.industry,
|
||||
website: updatedRaw.website,
|
||||
phone: updatedRaw.phone,
|
||||
address: updatedRaw.address,
|
||||
city: updatedRaw.city,
|
||||
state: updatedRaw.state,
|
||||
founded: updatedRaw.founded,
|
||||
linkedin: updatedRaw.linkedin,
|
||||
logo: updatedRaw.logo,
|
||||
version,
|
||||
})
|
||||
});
|
||||
if (!res.ok) { const d = await res.json(); throw new Error(d.error || 'Save failed'); }
|
||||
closeEditOrgModal();
|
||||
loadOrgs();
|
||||
} catch(e) {
|
||||
errEl.textContent = e.message;
|
||||
errEl.classList.remove('hidden');
|
||||
} finally {
|
||||
btn.disabled = false; btn.textContent = 'Save Changes';
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Org card menu ----
|
||||
function toggleOrgMenu(dealOrgId) {
|
||||
const drop = document.getElementById('orgMenuDrop_' + dealOrgId);
|
||||
|
|
|
|||
Loading…
Reference in New Issue