3042ddca38
- /wa-inbox, /wa-sessions e /wa-secretaria rodam sem a sidebar do scoreodonto (isStandaloneView); cada tela tem seu "Voltar" (Secretária ganhou botão na ThinNav). - SessionsView: banner "Você já possui acesso ao WhatsApp" quando há sessão ativa. - avatares: usa a URL do CDN (contactAvatarUrl/instance.avatar) direto; Avatar exibe sem depender de version; self-heal no onError re-busca a foto atual. - deps: framer-motion, immer. Dev override com HMR (docker-compose.dev.yml). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
// Cliente base do satélite NewWhats → /api/nw/v1/* (proxy do backend → motor
|
|
// /api/ext/v1/*). Auth: Bearer do scoreodonto (localStorage SCOREODONTO_AUTH_TOKEN).
|
|
// Retorna JSON já parseado; em erro, lança Error com `.response = { status, data }`
|
|
// (mesmo shape do axios, que o código portado do motor espera).
|
|
const API: string = (import.meta as any).env?.VITE_API_URL || '/api';
|
|
const TOKEN_KEY = 'SCOREODONTO_AUTH_TOKEN';
|
|
|
|
export function nwToken(): string | null {
|
|
try { return localStorage.getItem(TOKEN_KEY); } catch { return null; }
|
|
}
|
|
|
|
function authHeaders(extra?: Record<string, string>): Record<string, string> {
|
|
const t = nwToken();
|
|
return { ...(t ? { Authorization: `Bearer ${t}` } : {}), ...(extra || {}) };
|
|
}
|
|
|
|
async function req(method: string, path: string, body?: any): Promise<any> {
|
|
const isForm = typeof FormData !== 'undefined' && body instanceof FormData;
|
|
const res = await fetch(`${API}/nw/v1${path}`, {
|
|
method,
|
|
headers: authHeaders(isForm ? undefined : (body != null ? { 'Content-Type': 'application/json' } : undefined)),
|
|
body: body == null ? undefined : (isForm ? body : JSON.stringify(body)),
|
|
});
|
|
const text = await res.text();
|
|
let data: any = null;
|
|
try { data = text ? JSON.parse(text) : null; } catch { data = text; }
|
|
if (!res.ok) {
|
|
const err: any = new Error((data && data.error) || `${res.status}`);
|
|
err.response = { status: res.status, data };
|
|
throw err;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
export const nw = {
|
|
get: (p: string) => req('GET', p),
|
|
post: (p: string, body?: any) => req('POST', p, body),
|
|
put: (p: string, body?: any) => req('PUT', p, body),
|
|
patch: (p: string, body?: any) => req('PATCH', p, body),
|
|
delete: (p: string) => req('DELETE', p),
|
|
// URL base ('/api' relativo ou absoluta) — usada pelo socketService p/ o WS.
|
|
base: () => API,
|
|
};
|