diff --git a/internal/db/migrate.go b/internal/db/migrate.go index 7466244..89398d1 100644 --- a/internal/db/migrate.go +++ b/internal/db/migrate.go @@ -206,6 +206,8 @@ CREATE TABLE IF NOT EXISTS invites ( var additiveMigrationStmts = []string{ // Section 1: org_type `ALTER TABLE organizations ADD COLUMN org_type TEXT DEFAULT 'company'`, + // Section 4: industry + `ALTER TABLE deals ADD COLUMN industry TEXT DEFAULT ''`, } func seed(db *sql.DB) error { diff --git a/internal/handler/admin.go b/internal/handler/admin.go index 03e7416..650e361 100644 --- a/internal/handler/admin.go +++ b/internal/handler/admin.go @@ -168,8 +168,8 @@ func (h *Handler) handleAdminDealForm(w http.ResponseWriter, r *http.Request) { var deal model.Deal deal.Currency = "USD" if id != "" { - h.db.QueryRow("SELECT id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, exclusivity_end_date, expected_close_date, close_probability, is_archived FROM deals WHERE id = ? AND organization_id = ?", id, profile.OrganizationID).Scan( - &deal.ID, &deal.Name, &deal.Description, &deal.TargetCompany, &deal.Stage, &deal.DealSize, &deal.Currency, &deal.IOIDate, &deal.LOIDate, &deal.ExclusivityEnd, &deal.ExpectedCloseDate, &deal.CloseProbability, &deal.IsArchived) + h.db.QueryRow("SELECT id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, exclusivity_end_date, expected_close_date, close_probability, COALESCE(industry, ''), is_archived FROM deals WHERE id = ? AND organization_id = ?", id, profile.OrganizationID).Scan( + &deal.ID, &deal.Name, &deal.Description, &deal.TargetCompany, &deal.Stage, &deal.DealSize, &deal.Currency, &deal.IOIDate, &deal.LOIDate, &deal.ExclusivityEnd, &deal.ExpectedCloseDate, &deal.CloseProbability, &deal.Industry, &deal.IsArchived) } templates.AdminDealForm(profile, &deal).Render(r.Context(), w) @@ -191,6 +191,7 @@ func (h *Handler) handleAdminDealSave(w http.ResponseWriter, r *http.Request) { exclusivityEnd := r.FormValue("exclusivity_end") expectedClose := r.FormValue("expected_close_date") closeProbability, _ := strconv.Atoi(r.FormValue("close_probability")) + industry := strings.TrimSpace(r.FormValue("industry")) isArchived := r.FormValue("is_archived") == "on" if name == "" { @@ -206,15 +207,15 @@ func (h *Handler) handleAdminDealSave(w http.ResponseWriter, r *http.Request) { if id == "" { id = generateID("deal") - _, err := h.db.Exec(`INSERT INTO deals (id, organization_id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, exclusivity_end_date, expected_close_date, close_probability, is_archived, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - id, profile.OrganizationID, name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, exclusivityEnd, expectedClose, closeProbability, isArchived, profile.ID) + _, err := h.db.Exec(`INSERT INTO deals (id, organization_id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, exclusivity_end_date, expected_close_date, close_probability, industry, is_archived, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + id, profile.OrganizationID, name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, exclusivityEnd, expectedClose, closeProbability, industry, isArchived, profile.ID) if err != nil { http.Error(w, fmt.Sprintf("Error creating deal: %v", err), 500) return } } else { - _, err := h.db.Exec(`UPDATE deals SET name=?, description=?, target_company=?, stage=?, deal_size=?, currency=?, ioi_date=?, loi_date=?, exclusivity_end_date=?, expected_close_date=?, close_probability=?, is_archived=?, updated_at=CURRENT_TIMESTAMP WHERE id=? AND organization_id=?`, - name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, exclusivityEnd, expectedClose, closeProbability, isArchived, id, profile.OrganizationID) + _, err := h.db.Exec(`UPDATE deals SET name=?, description=?, target_company=?, stage=?, deal_size=?, currency=?, ioi_date=?, loi_date=?, exclusivity_end_date=?, expected_close_date=?, close_probability=?, industry=?, is_archived=?, updated_at=CURRENT_TIMESTAMP WHERE id=? AND organization_id=?`, + name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, exclusivityEnd, expectedClose, closeProbability, industry, isArchived, id, profile.OrganizationID) if err != nil { http.Error(w, fmt.Sprintf("Error updating deal: %v", err), 500) return diff --git a/internal/handler/deals.go b/internal/handler/deals.go index 0a3ab89..c0ce8e7 100644 --- a/internal/handler/deals.go +++ b/internal/handler/deals.go @@ -126,16 +126,58 @@ func (h *Handler) handleCreateDeal(w http.ResponseWriter, r *http.Request) { } ioiDate := r.FormValue("ioi_date") loiDate := r.FormValue("loi_date") + exclusivityEnd := r.FormValue("exclusivity_end") + industry := strings.TrimSpace(r.FormValue("industry")) description := strings.TrimSpace(r.FormValue("description")) + folderStructure := strings.TrimSpace(r.FormValue("folder_structure")) + inviteEmails := strings.TrimSpace(r.FormValue("invite_emails")) id := generateID("deal") - _, err := h.db.Exec(`INSERT INTO deals (id, organization_id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - id, profile.OrganizationID, name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, profile.ID) + _, err := h.db.Exec(`INSERT INTO deals (id, organization_id, name, description, target_company, stage, deal_size, currency, ioi_date, loi_date, exclusivity_end_date, industry, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + id, profile.OrganizationID, name, description, targetCompany, stage, dealSize, currency, ioiDate, loiDate, exclusivityEnd, industry, profile.ID) if err != nil { http.Error(w, fmt.Sprintf("Error creating deal: %v", err), 500) return } + // Create folder structure from textarea + if folderStructure != "" { + lines := strings.Split(folderStructure, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + parts := strings.Split(line, "/") + parentID := "" + for _, part := range parts { + part = strings.TrimSpace(part) + if part == "" { + continue + } + folderID := generateID("folder") + h.db.Exec("INSERT INTO folders (id, deal_id, parent_id, name, created_by) VALUES (?, ?, ?, ?, ?)", + folderID, id, parentID, part, profile.ID) + parentID = folderID + } + } + } + + // Create invites for initial team + if inviteEmails != "" { + emails := strings.Split(inviteEmails, "\n") + for _, email := range emails { + email = strings.TrimSpace(email) + if email == "" { + continue + } + token := generateToken() + expiresAt := time.Now().Add(7 * 24 * time.Hour) + h.db.Exec("INSERT INTO invites (token, org_id, email, role, invited_by, expires_at) VALUES (?, ?, ?, ?, ?, ?)", + token, profile.OrganizationID, email, "member", profile.ID, expiresAt) + } + } + http.Redirect(w, r, "/deals/"+id, http.StatusSeeOther) } diff --git a/internal/model/models.go b/internal/model/models.go index af22adb..e575d5e 100644 --- a/internal/model/models.go +++ b/internal/model/models.go @@ -49,6 +49,7 @@ type Deal struct { ExclusivityEnd string ExpectedCloseDate string CloseProbability int + Industry string IsArchived bool CreatedBy string CreatedAt time.Time diff --git a/templates/admin.templ b/templates/admin.templ index 8941718..058ab37 100644 --- a/templates/admin.templ +++ b/templates/admin.templ @@ -235,6 +235,7 @@ templ AdminDealForm(profile *model.Profile, deal *model.Deal) { @formField("name", "Deal Name", "text", deal.Name, true) @formTextarea("description", "Description", deal.Description) @formField("target_company", "Target Company", "text", deal.TargetCompany, false) + @formField("industry", "Industry", "text", deal.Industry, false) @formSelect("stage", "Stage", deal.Stage, []SelectOption{ {Value: "pipeline", Label: "Pipeline"}, {Value: "loi", Label: "LOI Stage"}, diff --git a/templates/dashboard.templ b/templates/dashboard.templ index 78162c7..344c15c 100644 --- a/templates/dashboard.templ +++ b/templates/dashboard.templ @@ -106,59 +106,7 @@ templ Dashboard(profile *model.Profile, deals []*model.Deal, activities []*model -
+ @newRoomForm() } @@ -233,6 +181,80 @@ templ statCard(label, value, subtitle, iconType string) { } +templ newRoomForm() { + +} + templ activityIcon(actType string) {{ fmt.Sprintf("%d deal rooms", len(deals)) } across your organization.