275 lines
7.5 KiB
Go
275 lines
7.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func (h *Handler) handleCreateStatement(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
profile := getProfile(r.Context())
|
|
|
|
dealID := r.FormValue("deal_id")
|
|
title := strings.TrimSpace(r.FormValue("title"))
|
|
body := strings.TrimSpace(r.FormValue("body"))
|
|
|
|
if dealID == "" || title == "" || body == "" {
|
|
http.Error(w, "deal_id, title, and body are required", 400)
|
|
return
|
|
}
|
|
|
|
respID := generateID("resp")
|
|
_, err := h.db.Exec(
|
|
`INSERT INTO responses (id, deal_id, type, title, body, extraction_status, created_by) VALUES (?, ?, 'statement', ?, ?, 'pending', ?)`,
|
|
respID, dealID, title, body, profile.ID)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("Error creating statement: %v", err), 500)
|
|
return
|
|
}
|
|
|
|
// Enqueue for chunking + embedding + matching
|
|
if h.extractor != nil {
|
|
h.enqueueExtraction(respID, "", dealID)
|
|
}
|
|
|
|
http.Redirect(w, r, "/deals/"+dealID+"?tab=requests", http.StatusSeeOther)
|
|
}
|
|
|
|
func (h *Handler) handleConfirmLink(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
profile := getProfile(r.Context())
|
|
|
|
requestID := r.FormValue("request_id")
|
|
responseID := r.FormValue("response_id")
|
|
chunkID := r.FormValue("chunk_id")
|
|
|
|
if requestID == "" || responseID == "" || chunkID == "" {
|
|
http.Error(w, "Missing fields", 400)
|
|
return
|
|
}
|
|
|
|
_, err := h.db.Exec(
|
|
"UPDATE request_links SET confirmed = 1, confirmed_by = ?, confirmed_at = ? WHERE request_id = ? AND response_id = ? AND chunk_id = ?",
|
|
profile.ID, time.Now().UTC().Format("2006-01-02 15:04:05"), requestID, responseID, chunkID)
|
|
if err != nil {
|
|
http.Error(w, "Error confirming link", 500)
|
|
return
|
|
}
|
|
|
|
// Update request status to answered if not already
|
|
h.db.Exec("UPDATE diligence_requests SET status = 'answered' WHERE id = ? AND status != 'answered'", requestID)
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write([]byte(`<span class="text-xs text-green-400 font-medium">Confirmed</span>`))
|
|
}
|
|
|
|
func (h *Handler) handleRejectLink(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
requestID := r.FormValue("request_id")
|
|
responseID := r.FormValue("response_id")
|
|
chunkID := r.FormValue("chunk_id")
|
|
|
|
if requestID == "" || responseID == "" || chunkID == "" {
|
|
http.Error(w, "Missing fields", 400)
|
|
return
|
|
}
|
|
|
|
h.db.Exec("DELETE FROM request_links WHERE request_id = ? AND response_id = ? AND chunk_id = ?",
|
|
requestID, responseID, chunkID)
|
|
|
|
w.Header().Set("Content-Type", "text/html")
|
|
w.Write([]byte(`<span class="text-xs text-gray-500 font-medium">Rejected</span>`))
|
|
}
|
|
|
|
func (h *Handler) handlePendingLinks(w http.ResponseWriter, r *http.Request) {
|
|
dealID := strings.TrimPrefix(r.URL.Path, "/deals/responses/pending/")
|
|
if dealID == "" {
|
|
http.Error(w, "Missing deal ID", 400)
|
|
return
|
|
}
|
|
|
|
rows, err := h.db.Query(`
|
|
SELECT rl.request_id, rl.response_id, rl.chunk_id, rl.confidence,
|
|
dr.description, r.title, r.type
|
|
FROM request_links rl
|
|
JOIN diligence_requests dr ON rl.request_id = dr.id
|
|
JOIN responses r ON rl.response_id = r.id
|
|
WHERE dr.deal_id = ? AND rl.confirmed = 0 AND rl.auto_linked = 1
|
|
ORDER BY rl.confidence DESC
|
|
`, dealID)
|
|
if err != nil {
|
|
http.Error(w, "Error loading pending links", 500)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
type pendingLink struct {
|
|
RequestID string `json:"request_id"`
|
|
ResponseID string `json:"response_id"`
|
|
ChunkID string `json:"chunk_id"`
|
|
Confidence float64 `json:"confidence"`
|
|
RequestDesc string `json:"request_desc"`
|
|
ResponseTitle string `json:"response_title"`
|
|
ResponseType string `json:"response_type"`
|
|
}
|
|
|
|
var links []pendingLink
|
|
for rows.Next() {
|
|
var l pendingLink
|
|
rows.Scan(&l.RequestID, &l.ResponseID, &l.ChunkID, &l.Confidence,
|
|
&l.RequestDesc, &l.ResponseTitle, &l.ResponseType)
|
|
links = append(links, l)
|
|
}
|
|
|
|
if links == nil {
|
|
links = []pendingLink{}
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(links)
|
|
}
|
|
|
|
func (h *Handler) handleSaveAssignmentRules(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
dealID := r.FormValue("deal_id")
|
|
rulesJSON := r.FormValue("rules")
|
|
|
|
if dealID == "" {
|
|
http.Error(w, "Missing deal_id", 400)
|
|
return
|
|
}
|
|
|
|
type ruleInput struct {
|
|
Keyword string `json:"keyword"`
|
|
AssigneeID string `json:"assignee_id"`
|
|
}
|
|
var rules []ruleInput
|
|
if err := json.Unmarshal([]byte(rulesJSON), &rules); err != nil {
|
|
http.Error(w, "Invalid rules JSON", 400)
|
|
return
|
|
}
|
|
|
|
// Delete existing rules and insert new set
|
|
h.db.Exec("DELETE FROM assignment_rules WHERE deal_id = ?", dealID)
|
|
for _, rule := range rules {
|
|
if rule.Keyword == "" || rule.AssigneeID == "" {
|
|
continue
|
|
}
|
|
id := generateID("rule")
|
|
h.db.Exec("INSERT INTO assignment_rules (id, deal_id, keyword, assignee_id) VALUES (?, ?, ?, ?)",
|
|
id, dealID, rule.Keyword, rule.AssigneeID)
|
|
}
|
|
|
|
// Re-run auto-assignment
|
|
h.autoAssignByRules(dealID)
|
|
|
|
http.Redirect(w, r, "/deals/"+dealID+"?tab=requests", http.StatusSeeOther)
|
|
}
|
|
|
|
func (h *Handler) handleGetAssignmentRules(w http.ResponseWriter, r *http.Request) {
|
|
dealID := strings.TrimPrefix(r.URL.Path, "/deals/assignment-rules/")
|
|
if dealID == "" {
|
|
http.Error(w, "Missing deal ID", 400)
|
|
return
|
|
}
|
|
|
|
rows, err := h.db.Query(`
|
|
SELECT ar.id, ar.keyword, ar.assignee_id, COALESCE(p.full_name, ar.assignee_id)
|
|
FROM assignment_rules ar
|
|
LEFT JOIN profiles p ON ar.assignee_id = p.id
|
|
WHERE ar.deal_id = ?
|
|
ORDER BY ar.keyword
|
|
`, dealID)
|
|
if err != nil {
|
|
http.Error(w, "Error loading rules", 500)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
type ruleOut struct {
|
|
ID string `json:"id"`
|
|
Keyword string `json:"keyword"`
|
|
AssigneeID string `json:"assignee_id"`
|
|
AssigneeName string `json:"assignee_name"`
|
|
}
|
|
var rules []ruleOut
|
|
for rows.Next() {
|
|
var r ruleOut
|
|
rows.Scan(&r.ID, &r.Keyword, &r.AssigneeID, &r.AssigneeName)
|
|
rules = append(rules, r)
|
|
}
|
|
if rules == nil {
|
|
rules = []ruleOut{}
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
json.NewEncoder(w).Encode(rules)
|
|
}
|
|
|
|
// autoAssignByRules assigns unassigned requests based on keyword→assignee rules.
|
|
func (h *Handler) autoAssignByRules(dealID string) {
|
|
// Load rules
|
|
ruleRows, err := h.db.Query("SELECT keyword, assignee_id FROM assignment_rules WHERE deal_id = ?", dealID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer ruleRows.Close()
|
|
|
|
type rule struct {
|
|
keyword, assigneeID string
|
|
}
|
|
var rules []rule
|
|
for ruleRows.Next() {
|
|
var r rule
|
|
ruleRows.Scan(&r.keyword, &r.assigneeID)
|
|
rules = append(rules, r)
|
|
}
|
|
|
|
if len(rules) == 0 {
|
|
return
|
|
}
|
|
|
|
// Load unassigned requests
|
|
reqRows, err := h.db.Query("SELECT id, section, description FROM diligence_requests WHERE deal_id = ? AND (assignee_id = '' OR assignee_id IS NULL)", dealID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer reqRows.Close()
|
|
|
|
type reqInfo struct {
|
|
id, section, desc string
|
|
}
|
|
var reqs []reqInfo
|
|
for reqRows.Next() {
|
|
var r reqInfo
|
|
reqRows.Scan(&r.id, &r.section, &r.desc)
|
|
reqs = append(reqs, r)
|
|
}
|
|
|
|
for _, req := range reqs {
|
|
text := strings.ToLower(req.section + " " + req.desc)
|
|
for _, rule := range rules {
|
|
if strings.Contains(text, strings.ToLower(rule.keyword)) {
|
|
h.db.Exec("UPDATE diligence_requests SET assignee_id = ? WHERE id = ?", rule.assigneeID, req.id)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|