99 lines
3.7 KiB
JavaScript
99 lines
3.7 KiB
JavaScript
import apiClient from '../api/client';
|
|
|
|
const STAGE_SEQUENCE = [
|
|
{ key: 'queued', label: 'Preparing scrape', note: 'Setting up the onboarding job.' },
|
|
{ key: 'crawling', label: 'Scraping brand pages', note: 'Capturing the homepage, about page, and representative product pages.' },
|
|
{ key: 'summarizing', label: 'Building site summary', note: 'Condensing the scraped pages into a compact brand summary.' },
|
|
{ key: 'parsing_brand', label: 'Extracting brand context', note: 'Inferring tone, taglines, and visual signals.' },
|
|
{ key: 'finalizing_business', label: 'Finalizing business', note: 'Saving the captured data and storefront assets.' },
|
|
{ key: 'completed', label: 'Completed', note: 'The onboarding job finished successfully.' },
|
|
{ key: 'failed', label: 'Failed', note: 'The onboarding job could not be completed.' },
|
|
];
|
|
const JOB_LOOKUP_RETRY_LIMIT = 4;
|
|
|
|
const RETRYABLE_JOB_NOT_FOUND_WINDOW_MS = 20_000;
|
|
const MAX_RETRYABLE_JOB_NOT_FOUND_MISSES = 4;
|
|
|
|
export async function startBusinessOnboardingJob(payload) {
|
|
const response = await apiClient.post('/api/businesses', payload);
|
|
|
|
if (response.status !== 202 || !response.data?.jobId) {
|
|
throw new Error('Unexpected onboarding response');
|
|
}
|
|
|
|
return response.data;
|
|
}
|
|
|
|
export async function fetchBusinessOnboardingJob(jobId) {
|
|
const response = await apiClient.get(`/api/businesses/jobs/${encodeURIComponent(jobId)}`);
|
|
return response.data;
|
|
}
|
|
|
|
export function getBusinessOnboardingStageMeta(stage) {
|
|
const normalizedStage = String(stage || '').trim().toLowerCase();
|
|
const found = STAGE_SEQUENCE.find((entry) => entry.key === normalizedStage) || STAGE_SEQUENCE[0];
|
|
const index = STAGE_SEQUENCE.findIndex((entry) => entry.key === found.key);
|
|
const percent = normalizedStage === 'completed'
|
|
? 100
|
|
: normalizedStage === 'failed'
|
|
? 100
|
|
: Math.max(8, Math.min(92, index <= 0 ? 12 : 12 + (index * 20)));
|
|
|
|
return {
|
|
stage: found.key,
|
|
label: found.label,
|
|
note: found.note,
|
|
index,
|
|
percent,
|
|
totalStages: STAGE_SEQUENCE.length - 2,
|
|
};
|
|
}
|
|
|
|
export function getBusinessOnboardingProgress(job = {}) {
|
|
const progress = job?.progress && typeof job.progress === 'object' ? job.progress : {};
|
|
return {
|
|
pagesProcessed: Number(progress.pagesProcessed || 0),
|
|
pagesDiscovered: Number(progress.pagesDiscovered || 0),
|
|
creditsUsed: Number(progress.creditsUsed || 0),
|
|
representativePages: Number(progress.representativePages || 0),
|
|
imageCount: Number(progress.imageCount || 0),
|
|
linkCount: Number(progress.linkCount || 0),
|
|
};
|
|
}
|
|
|
|
export function getBusinessOnboardingError(job = {}) {
|
|
const error = job?.error;
|
|
if (!error) return '';
|
|
if (typeof error === 'string') return error;
|
|
if (typeof error === 'object') {
|
|
return String(error.message || error.error || error.details || '').trim();
|
|
}
|
|
|
|
return String(error).trim();
|
|
}
|
|
|
|
export function isRetryableOnboardingJobLookupError(error) {
|
|
if (error?.response?.status !== 404) return false;
|
|
|
|
const message = String(error?.response?.data?.error || error?.message || '').trim();
|
|
return /onboarding job not found/i.test(message);
|
|
}
|
|
|
|
export function shouldRetryOnboardingJobLookup(error, missCount = 0) {
|
|
return isRetryableOnboardingJobLookupError(error) && missCount < JOB_LOOKUP_RETRY_LIMIT;
|
|
}
|
|
|
|
export function getOnboardingJobRetryDelay(missCount = 0) {
|
|
return Math.min(4500, 1500 + (Math.max(0, missCount) * 700));
|
|
}
|
|
|
|
export function shouldRetryMissingBusinessOnboardingJob(job = {}, error, missCount = 0) {
|
|
if (error?.response?.status !== 404) return false;
|
|
if (missCount >= MAX_RETRYABLE_JOB_NOT_FOUND_MISSES) return false;
|
|
|
|
const createdAtMs = Date.parse(String(job?.createdAt || '').trim());
|
|
if (!Number.isFinite(createdAtMs)) return false;
|
|
|
|
return Date.now() - createdAtMs <= RETRYABLE_JOB_NOT_FOUND_WINDOW_MS;
|
|
}
|