hh-shipments 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/app.js +37 -6
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -111,7 +111,10 @@ async function loadDash() {
|
|
|
111
111
|
async function runQuick(text, targetEl, preset) {
|
|
112
112
|
targetEl.innerHTML = '<p class="muted">Extracting…</p>';
|
|
113
113
|
try {
|
|
114
|
-
const r = await
|
|
114
|
+
const r = await Promise.race([
|
|
115
|
+
api.post('/api/intake/quick', { text, seller_name: preset && preset.seller, device: preset && preset.device }),
|
|
116
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error('extract timed out — check your connection and try again')), 45000)),
|
|
117
|
+
]);
|
|
115
118
|
let html = '';
|
|
116
119
|
r.added.forEach((a) => { html += `<div class="review">✅ Added <b>${esc(a.number)}</b> (${esc(a.carrier.toUpperCase())})${r.simulated ? ' <span class="sim-badge">SIMULATED</span>' : ''}</div>`; });
|
|
117
120
|
r.duplicates.forEach((n) => { html += `<div class="review">⛔ <b>${esc(n)}</b> is already tracked — skipped.</div>`; });
|
|
@@ -171,13 +174,16 @@ async function ocrFile(file, statusEl) {
|
|
|
171
174
|
async function handlePhotoFile(f) {
|
|
172
175
|
const target = $('photo-result');
|
|
173
176
|
try {
|
|
177
|
+
f = await shrinkImage(f);
|
|
174
178
|
const url = URL.createObjectURL(f);
|
|
175
179
|
target.innerHTML = `<div class="review"><img src="${url}" style="max-width:100%;border-radius:10px"><p class="muted">📖 Reading screenshot…</p></div>`;
|
|
176
180
|
const text = await ocrFile(f, $('ocr-status'));
|
|
177
181
|
// Upload original for reference, then run the quick flow on OCR text
|
|
178
182
|
const fd = new FormData();
|
|
179
183
|
fd.append('image', f);
|
|
180
|
-
|
|
184
|
+
let up;
|
|
185
|
+
try { up = await fetchTimeout('/api/intake/image', { method: 'POST', body: fd }, 60000); }
|
|
186
|
+
catch (e) { throw new Error('upload timed out — check your connection and try again'); }
|
|
181
187
|
if (!up.ok) throw new Error('upload failed');
|
|
182
188
|
const r = await runQuick(text, $('photo-result'));
|
|
183
189
|
if (r && !r.added.length && !r.needs_review.length && !r.duplicates.length) showOcrRead($('photo-result'), text);
|
|
@@ -206,21 +212,46 @@ function pastedImageFiles(e) {
|
|
|
206
212
|
return files;
|
|
207
213
|
}
|
|
208
214
|
|
|
209
|
-
//
|
|
210
|
-
//
|
|
215
|
+
// Shrink huge phone photos before OCR/upload: much faster to read and send,
|
|
216
|
+
// and less exposed to a stalled connection. Falls back to the original file.
|
|
217
|
+
async function shrinkImage(file, maxDim = 1600) {
|
|
218
|
+
try {
|
|
219
|
+
const bmp = await createImageBitmap(file);
|
|
220
|
+
const scale = Math.min(1, maxDim / Math.max(bmp.width, bmp.height));
|
|
221
|
+
if (scale >= 1) { bmp.close(); return file; }
|
|
222
|
+
const canvas = document.createElement('canvas');
|
|
223
|
+
canvas.width = Math.round(bmp.width * scale);
|
|
224
|
+
canvas.height = Math.round(bmp.height * scale);
|
|
225
|
+
canvas.getContext('2d').drawImage(bmp, 0, 0, canvas.width, canvas.height);
|
|
226
|
+
bmp.close();
|
|
227
|
+
const blob = await new Promise((res) => canvas.toBlob(res, 'image/jpeg', 0.85));
|
|
228
|
+
return blob ? new File([blob], 'label.jpg', { type: 'image/jpeg' }) : file;
|
|
229
|
+
} catch (e) { return file; }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function fetchTimeout(url, opts = {}, ms = 60000) {
|
|
233
|
+
const c = new AbortController();
|
|
234
|
+
const t = setTimeout(() => c.abort(), ms);
|
|
235
|
+
try { return await fetch(url, { ...opts, signal: c.signal }); }
|
|
236
|
+
finally { clearTimeout(t); }
|
|
237
|
+
}
|
|
211
238
|
async function handlePastedImageFile(f) {
|
|
212
239
|
const target = $('quick-result');
|
|
213
240
|
try {
|
|
214
241
|
// Instant feedback: show a thumbnail + progress the moment the image lands
|
|
242
|
+
f = await shrinkImage(f);
|
|
215
243
|
const url = URL.createObjectURL(f);
|
|
216
244
|
target.innerHTML = `<div class="review"><img src="${url}" style="max-width:100%;border-radius:10px"><p class="muted" id="ocr-live">📖 Reading screenshot…</p></div>`;
|
|
217
245
|
const text = await ocrFile(f, $('ocr-status-paste'));
|
|
218
246
|
const live = $('ocr-live');
|
|
219
|
-
if (live) live.textContent = '
|
|
247
|
+
if (live) live.textContent = '⬆️ Uploading…';
|
|
220
248
|
const fd = new FormData();
|
|
221
249
|
fd.append('image', f);
|
|
222
|
-
|
|
250
|
+
let up;
|
|
251
|
+
try { up = await fetchTimeout('/api/intake/image', { method: 'POST', body: fd }, 60000); }
|
|
252
|
+
catch (e) { throw new Error('upload timed out — check your connection and try again'); }
|
|
223
253
|
if (!up.ok) throw new Error('upload failed');
|
|
254
|
+
if (live) live.textContent = '🔍 Finding tracking number…';
|
|
224
255
|
const r = await runQuick(text, target);
|
|
225
256
|
if (r && !r.added.length && !r.needs_review.length && !r.duplicates.length) showOcrRead(target, text);
|
|
226
257
|
} catch (err) { $('ocr-status-paste').textContent = ''; target.innerHTML = `<p class="error">${esc(err.message)}</p>`; }
|