pos implemented
This commit is contained in:
@@ -23,7 +23,20 @@
|
||||
* Components call the hooks in `./posQueries`, not these functions.
|
||||
*/
|
||||
|
||||
const POS_BASE = import.meta.env.VITE_FIESTA_POS_URL || 'https://fiesta.nearle.app/live/api/v1/pos';
|
||||
/**
|
||||
* The console reads counter sales from `/v1/web/pos`, not `/v1/pos`.
|
||||
*
|
||||
* `/v1/pos` is the terminal group and sits behind `PosAuth`, which pins a
|
||||
* request to the outlet inside a till's token. A browser has no such token and
|
||||
* cannot get one — `/pos/login` refuses any account that is not a till account.
|
||||
* So the day `POS_AUTH_REQUIRED=true` is set, every read on that group would
|
||||
* 401 and this page would go blank.
|
||||
*
|
||||
* `/v1/web/pos` serves the same five handlers outside the guard. Identical
|
||||
* responses — they read their scope from query parameters and never touch the
|
||||
* session — so nothing else in this client changes.
|
||||
*/
|
||||
const POS_BASE = import.meta.env.VITE_FIESTA_POS_URL || 'https://fiesta.nearle.app/live/api/v1/web/pos';
|
||||
|
||||
type QueryParams = Record<string, string | number | undefined | null>;
|
||||
|
||||
@@ -136,6 +149,48 @@ export interface PosSalesFilter {
|
||||
pagesize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* The terminal group, used only until the console base exists on a server.
|
||||
*
|
||||
* TRANSITIONAL. The frontend and the backend deploy separately and the backend
|
||||
* is currently behind, so shipping this build first would point every read at a
|
||||
* route that answers 404 and blank the page. Rather than make the two deploys
|
||||
* ordered — a constraint nobody would remember in six months — the base is
|
||||
* resolved once at runtime: try the console group, and if the server is old
|
||||
* enough not to have it, fall back to the terminal group for this session.
|
||||
*
|
||||
* Safe to delete once every environment is past the deploy that added
|
||||
* `/v1/web/pos`. It is a no-op there: the first probe succeeds and the fallback
|
||||
* is never reached.
|
||||
*/
|
||||
const POS_BASE_LEGACY = POS_BASE.replace('/v1/web/pos', '/v1/pos').replace('/v1/mob/pos', '/v1/pos');
|
||||
|
||||
let resolvedBase: string | null = POS_BASE === POS_BASE_LEGACY ? POS_BASE : null;
|
||||
let baseProbe: Promise<string> | null = null;
|
||||
|
||||
/** Resolves the base once per session; concurrent callers share one probe. */
|
||||
async function posBase(): Promise<string> {
|
||||
if (resolvedBase) return resolvedBase;
|
||||
if (!baseProbe) {
|
||||
baseProbe = (async () => {
|
||||
try {
|
||||
// `sales/summary` needs no data to exist — a missing route 404s, a
|
||||
// present one answers 200 even for an outlet with no bills.
|
||||
const res = await fetch(`${POS_BASE}/sales/summary?locationid=0`, {
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
resolvedBase = res.status === 404 ? POS_BASE_LEGACY : POS_BASE;
|
||||
} catch {
|
||||
// A network failure says nothing about which routes exist; assume the
|
||||
// current one and let the real request surface the error.
|
||||
resolvedBase = POS_BASE;
|
||||
}
|
||||
return resolvedBase;
|
||||
})();
|
||||
}
|
||||
return baseProbe;
|
||||
}
|
||||
|
||||
// ── Transport ────────────────────────────────────────────────────────────────
|
||||
|
||||
async function posGet<T = unknown>(endpoint: string, params: QueryParams = {}): Promise<T> {
|
||||
@@ -144,7 +199,8 @@ async function posGet<T = unknown>(endpoint: string, params: QueryParams = {}):
|
||||
if (v !== undefined && v !== null && v !== '') qs.append(k, String(v));
|
||||
});
|
||||
const query = qs.toString();
|
||||
const res = await fetch(`${POS_BASE}/${endpoint}${query ? `?${query}` : ''}`, {
|
||||
const base = await posBase();
|
||||
const res = await fetch(`${base}/${endpoint}${query ? `?${query}` : ''}`, {
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
if (!res.ok) {
|
||||
|
||||
Reference in New Issue
Block a user