Preuve · code, extrait

src/dashboard/api/client.ts

Dépôt jeremy-flexylead/flexylead-crm · commit ccbbaa07870e0f786729003dd6f8862e4bcc8256 · dossier de production synchronisé le 14 septembre 2026 à 08 h 46

Extrait tel qu'à ce commit : lignes 7009–7080, sur 7 087 (numérotation d'origine, 30 lignes de contexte autour de chaque citation) · fichier intégral sur GitHub (dépôt privé, accès requis)

Lignes citées par l'audit : 7039–7050

72 lignes reproduites · ← retour à l'audit · catalogue des preuves

7009      contrat_groupe_partage: fields.contrat_groupe_partage ?? null,
7010      contrat_labels: fields.contrat_labels ?? [],
7011    }] });
7012}
7013
7014export async function updateOffre(localId: string, fields: Partial<OffreFields>): Promise<void> {
7015  const id = await findOffreRowId(localId);
7016  if (!id) throw new Error('offre not found: ' + localId);
7017  await adminDataMutation('update', 'offres', { id, fields });
7018}
7019
7020export async function replaceOfferRemunerationRules(offreId: string, rules: OfferRemunerationRuleFields[], removedRuleIds: string[] = []): Promise<void> {
7021  const cleanOfferId = offreId.trim();
7022  if (!cleanOfferId) throw new Error('Offre requise pour la rémunération.');
7023  await activeBusinessAction<OfferCatalogSaveResult>('offer-catalog.manage', {
7024    action: 'save_pricing',
7025    offer_id: cleanOfferId,
7026    rules,
7027    removed_rule_ids: removedRuleIds,
7028  });
7029}
7030
7031// ── Manual rapport correction (human-in-the-loop) ──────────────────────
7032// The dedicated quality command validates revisions, computes scores and records the actor.
7033// Writes corrected evaluations + recomputed score back into the EXISTING
7034// rapports columns (evaluations JSON + score_total). The audit trail
7035// (ai_verdict / corrected_by / corrected_at) lives inside the evaluations
7036// JSON, so no table-schema change is needed. All dashboard metrics read
7037// these same fields, so a correction propagates everywhere automatically.
7038export async function correctRapport(
7039  rapportLocalId: string,
7040  fields: { evaluations: Evaluation[]; score_total: number; expected_modified_time?: string },
7041): Promise<{ evaluations: Evaluation[]; score_total: number; modified_time: string }> {
7042  const detail=await apiPost<{row:{modified_time:string;evaluations:Evaluation[]} | null}>('/v1/quality/query',{action:'rapport_detail',rapport_id:rapportLocalId});
7043  if(!detail.row)throw new Error('Rapport introuvable.');
7044  const original=typeof detail.row.evaluations==='string'?JSON.parse(detail.row.evaluations):detail.row.evaluations;
7045  const corrections=fields.evaluations.flatMap((e,index)=>e.verdict===original[index]?.verdict&&e.commentaire===original[index]?.commentaire?[]:[{index,verdict:e.verdict,commentaire:e.commentaire}]);
7046  if(corrections.some(e=>!e.commentaire?.trim()))throw new Error('Expliquez chaque correction avant de l’enregistrer.');
7047  const result=await apiPost<{row:{evaluations:Evaluation[];score_total:number;modified_time:string}}>('/v1/quality/query',{action:'rapport_correct',rapport_id:rapportLocalId,expected_modified_time:fields.expected_modified_time,corrections});
7048  return result.row;
7049}
7050
7051// Update the list of offres a centre is configured to propose. Stored in the
7052// centres.offres_proposees json column (added 2026-05-21). Survives sync runs
7053// (centres upsert is deleteMissing:false + diffFields:[]).
7054export async function updateCentreOffres(centreLocalId: string, offreIds: string[]): Promise<void> {
7055  const rowId = await findCentreRowId(centreLocalId);
7056  await adminDataMutation('update', 'centres', { id: rowId, fields: { offres_proposees: offreIds } });
7057}
7058
7059export function partnerUrl(token: string): string {
7060  return `${window.location.origin}/partner/${token}`;
7061}
7062
7063export function useTranscriptText(_centreId: string | null, appelId: string | null) {
7064  return useQuery({
7065    queryKey: ['transcript-text', appelId],
7066    queryFn: async () => {
7067      if (!appelId) return { text: '' };
7068      const r = await apteva.app('tables').tool<{ rows: Array<{ transcript_text?: string }> }>(
7069        'rows_search',
7070        {
7071          table: 'appels',
7072          where: [{ col: 'local_id', op: 'eq', value: appelId }],
7073          limit: 1,
7074          _project_id: PROJECT_ID,
7075        },
7076      );
7077      return { text: r.rows?.[0]?.transcript_text || '' };
7078    },
7079    enabled: !!appelId,
7080    staleTime: 60_000,