File size: 4,046 Bytes
7f6dd09
 
d061a75
 
7f6dd09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26159c8
 
 
 
7f6dd09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702b8ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f6dd09
 
 
 
 
 
d061a75
 
 
7f6dd09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import axios from "axios";

const configuredBaseUrl = import.meta.env.VITE_API_URL?.trim();
const BASE_URL = configuredBaseUrl || "";

const api = axios.create({ baseURL: BASE_URL, timeout: 30000 });

api.interceptors.response.use(
  (response) => response,
  (error) => {
    const message =
      error.response?.data?.error || error.message || "Request failed";
    return Promise.reject(new Error(message));
  },
);

export async function login(email, password) {
  const res = await api.post("/api/auth/login", { email, password });
  return res.data;
}

export async function signup(email, password) {
  const res = await api.post("/api/auth/signup", { email, password });
  return res.data;
}

export async function logout() {
  const res = await api.post("/api/auth/logout");
  return res.data;
}

export async function createJob(companyName, userEmail) {
  const res = await api.post("/api/jobs", {
    company_name: companyName,
    user_email: userEmail,
  });
  return res.data;
}

export async function uploadFiles(jobId, formData) {
  const res = await api.post("/api/upload/" + jobId, formData, {
    headers: { "Content-Type": "multipart/form-data" },
    timeout: 120000,
  });
  return res.data;
}

export async function getJobResult(jobId) {
  const res = await api.get("/api/analysis/" + jobId + "/result");
  return res.data;
}

export async function getJobStatus(jobId) {
  const res = await api.get("/api/jobs/" + jobId + "/status");
  return res.data;
}

export async function submitOfficerNotes(jobId, notes, officerId) {
  const res = await api.post("/api/officer/" + jobId + "/notes", {
    notes,
    officer_id: officerId || "anonymous",
  });
  return res.data;
}

export async function getCAM(jobId) {
  const res = await api.get("/api/cam/" + jobId);
  return res.data;
}

export async function regenerateCAM(jobId) {
  const res = await api.post("/api/cam/" + jobId + "/regenerate");
  return res.data;
}

export async function downloadCAM(jobId, format) {
  const res = await api.get(`/api/cam/${jobId}/download/${format}`, {
    responseType: "blob",
    timeout: 60000,
  });
  const mime =
    format === "pdf"
      ? "application/pdf"
      : "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
  const blob = new Blob([res.data], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `${jobId}_Credit_Memo.${format}`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

export async function getCAMPdfBlobUrl(jobId) {
  const res = await api.get(`/api/cam/${jobId}/download/pdf?preview=true`, {
    responseType: "blob",
    timeout: 60000,
  });
  const blob = new Blob([res.data], { type: "application/pdf" });
  return URL.createObjectURL(blob);
}

export async function listJobs() {
  const res = await api.get("/api/jobs");
  return res.data;
}

export function createSSEConnection(jobId, onEvent) {
  const url = BASE_URL
    ? BASE_URL + "/api/analysis/" + jobId + "/stream"
    : "/api/analysis/" + jobId + "/stream";
  const source = new EventSource(url);
  let connected = false;
  let done = false;

  source.onopen = () => {
    connected = true;
  };

  source.onmessage = (e) => {
    try {
      const parsed = JSON.parse(e.data);
      onEvent(parsed);

      // If server sent complete or error, we're done — close cleanly
      if (parsed.type === "complete" || parsed.type === "error") {
        done = true;
        source.close();
      }
    } catch (err) {
      console.error("SSE parse error", err);
    }
  };

  source.onerror = () => {
    source.close();
    // Don't fire error if we already received a terminal event
    if (done) return;

    onEvent({
      type: "error",
      stage: "SSE",
      message: connected
        ? "Connection to pipeline lost. Please try again."
        : "Unable to connect to analysis pipeline. Check that the backend is running.",
      percent: 0,
    });
  };

  return source;
}