v0.1.12
This commit is contained in:
+40
-47
@@ -1,16 +1,5 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { browser } from '$app/environment';
|
||||
import {
|
||||
mockClientAccess,
|
||||
mockClientAccessExport,
|
||||
mockCosts,
|
||||
mockMixCalculatorOptions,
|
||||
mockMixCalculatorSessions,
|
||||
mockMixes,
|
||||
mockProducts,
|
||||
mockRawMaterials,
|
||||
mockScenarios
|
||||
} from '$lib/mock';
|
||||
import type {
|
||||
ClientAccessAccount,
|
||||
ClientAccessPowerBiExport,
|
||||
@@ -34,6 +23,9 @@ import type {
|
||||
MixUpdateInput,
|
||||
Product,
|
||||
ProductCostBreakdown,
|
||||
ProductCostingInputs,
|
||||
ProductCostingItem,
|
||||
ProductCostingItemUpdateInput,
|
||||
RawMaterial,
|
||||
RawMaterialCreateInput,
|
||||
RawMaterialPriceCreateInput,
|
||||
@@ -136,23 +128,17 @@ function normalizeRequestError(error: unknown) {
|
||||
return new Error('An unexpected error occurred while contacting the server.');
|
||||
}
|
||||
|
||||
async function fetchJson<T>(path: string, fallback: T, auth: AuthMode = 'none', fetcher: ApiFetch = fetch): Promise<T> {
|
||||
async function fetchJson<T>(path: string, auth: AuthMode = 'none', fetcher: ApiFetch = fetch): Promise<T> {
|
||||
try {
|
||||
const response = await fetcher(resolveRequestUrl(path, fetcher), {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) {
|
||||
if (auth !== 'none') {
|
||||
throw new Error(response.statusText || 'Unauthorized');
|
||||
}
|
||||
return fallback;
|
||||
throw new Error(response.statusText || 'Request failed');
|
||||
}
|
||||
return (await response.json()) as T;
|
||||
} catch (error) {
|
||||
if (auth !== 'none') {
|
||||
throw normalizeRequestError(error);
|
||||
}
|
||||
return fallback;
|
||||
throw normalizeRequestError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,13 +158,12 @@ function makeCacheKey(path: string, auth: AuthMode) {
|
||||
|
||||
async function cachedFetchJson<T>(
|
||||
path: string,
|
||||
fallback: T,
|
||||
auth: AuthMode = 'none',
|
||||
fetcher: ApiFetch = fetch
|
||||
): Promise<T> {
|
||||
// Bypass the cache during SSR (no localStorage, no shared session).
|
||||
if (!browser) {
|
||||
return fetchJson<T>(path, fallback, auth, fetcher);
|
||||
return fetchJson<T>(path, auth, fetcher);
|
||||
}
|
||||
|
||||
const key = makeCacheKey(path, auth);
|
||||
@@ -194,7 +179,7 @@ async function cachedFetchJson<T>(
|
||||
return existing as Promise<T>;
|
||||
}
|
||||
|
||||
const promise = fetchJson<T>(path, fallback, auth, fetcher)
|
||||
const promise = fetchJson<T>(path, auth, fetcher)
|
||||
.then((value) => {
|
||||
responseCache.set(key, { value, expiresAt: Date.now() + READ_CACHE_TTL_MS });
|
||||
return value;
|
||||
@@ -290,13 +275,13 @@ async function requestBlob(
|
||||
}
|
||||
|
||||
export const api = {
|
||||
rawMaterials: (fetcher?: ApiFetch) => cachedFetchJson<RawMaterial[]>('/api/raw-materials', mockRawMaterials, 'client', fetcher),
|
||||
mixes: (fetcher?: ApiFetch) => cachedFetchJson('/api/mixes', mockMixes, 'client', fetcher),
|
||||
rawMaterials: (fetcher?: ApiFetch) => cachedFetchJson<RawMaterial[]>('/api/raw-materials', 'client', fetcher),
|
||||
mixes: (fetcher?: ApiFetch) => cachedFetchJson<Mix[]>('/api/mixes', 'client', fetcher),
|
||||
mix: (mixId: number, fetcher?: ApiFetch) => request<Mix>(`/api/mixes/${mixId}`, { method: 'GET' }, 'client', fetcher),
|
||||
mixCalculatorOptions: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<MixCalculatorOptions>('/api/mix-calculator/options', mockMixCalculatorOptions, 'client', fetcher),
|
||||
cachedFetchJson<MixCalculatorOptions>('/api/mix-calculator/options', 'client', fetcher),
|
||||
mixCalculatorSessions: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<MixCalculatorSession[]>('/api/mix-calculator', mockMixCalculatorSessions, 'client', fetcher),
|
||||
cachedFetchJson<MixCalculatorSession[]>('/api/mix-calculator', 'client', fetcher),
|
||||
mixCalculatorSession: (sessionId: number, fetcher?: ApiFetch) =>
|
||||
request<MixCalculatorSession>(`/api/mix-calculator/${sessionId}`, { method: 'GET' }, 'client', fetcher),
|
||||
mixCalculatorSessionPdf: (sessionId: number, fetcher?: ApiFetch) =>
|
||||
@@ -321,7 +306,7 @@ export const api = {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
products: (fetcher?: ApiFetch) => cachedFetchJson<Product[]>('/api/products', mockProducts, 'client', fetcher),
|
||||
products: (fetcher?: ApiFetch) => cachedFetchJson<Product[]>('/api/products', 'client', fetcher),
|
||||
editorProducts: (params?: { q?: string; client_name?: string; limit?: number }, fetcher?: ApiFetch) => {
|
||||
const search = new URLSearchParams();
|
||||
if (params?.q) search.set('q', params.q);
|
||||
@@ -329,7 +314,7 @@ export const api = {
|
||||
if (params?.limit) search.set('limit', String(params.limit));
|
||||
const qs = search.toString();
|
||||
const path = qs ? `/api/editor/products?${qs}` : '/api/editor/products';
|
||||
return cachedFetchJson<EditorProductRow[]>(path, [], 'client', fetcher);
|
||||
return cachedFetchJson<EditorProductRow[]>(path, 'client', fetcher);
|
||||
},
|
||||
updateEditorProduct: (productId: number, payload: EditorProductUpdateInput) =>
|
||||
request<EditorProductRow>(`/api/editor/products/${productId}`, {
|
||||
@@ -358,10 +343,28 @@ export const api = {
|
||||
method: 'DELETE'
|
||||
}, 'client'),
|
||||
productCosts: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<ProductCostBreakdown[]>('/api/powerbi/product-costs', mockCosts, 'client', fetcher),
|
||||
scenarios: (fetcher?: ApiFetch) => cachedFetchJson<Scenario[]>('/api/scenarios', mockScenarios, 'client', fetcher),
|
||||
cachedFetchJson<ProductCostBreakdown[]>('/api/powerbi/product-costs', 'client', fetcher),
|
||||
productCostingItems: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<ProductCostingItem[]>('/api/product-costing/items', 'client', fetcher),
|
||||
productCostingItemsFresh: () =>
|
||||
request<ProductCostingItem[]>(`/api/product-costing/items?_=${Date.now()}`, { method: 'GET' }, 'client'),
|
||||
productCostingInputs: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<ProductCostingInputs>('/api/product-costing/inputs', 'client', fetcher),
|
||||
updateProductCostingInputs: (payload: Partial<ProductCostingInputs>) =>
|
||||
request<ProductCostingInputs>('/api/product-costing/inputs', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
updateProductCostingItem: (itemId: number, payload: ProductCostingItemUpdateInput) =>
|
||||
request<ProductCostingItem>(`/api/product-costing/items/${itemId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
recalculateProductCosting: () =>
|
||||
request<{ recalculated: number }>('/api/product-costing/recalculate-all', { method: 'POST' }, 'client'),
|
||||
scenarios: (fetcher?: ApiFetch) => cachedFetchJson<Scenario[]>('/api/scenarios', 'client', fetcher),
|
||||
throughputProducts: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<ThroughputProduct[]>('/api/throughput/products', [], 'client', fetcher),
|
||||
cachedFetchJson<ThroughputProduct[]>('/api/throughput/products', 'client', fetcher),
|
||||
throughputEntries: (params?: ThroughputEntryListParams, fetcher?: ApiFetch) => {
|
||||
const search = new URLSearchParams();
|
||||
if (params?.date_from) search.set('date_from', params.date_from);
|
||||
@@ -372,7 +375,7 @@ export const api = {
|
||||
if (params?.limit) search.set('limit', String(params.limit));
|
||||
const qs = search.toString();
|
||||
const path = qs ? `/api/throughput/entries?${qs}` : '/api/throughput/entries';
|
||||
return cachedFetchJson<ThroughputEntry[]>(path, [], 'client', fetcher);
|
||||
return cachedFetchJson<ThroughputEntry[]>(path, 'client', fetcher);
|
||||
},
|
||||
createThroughputEntry: (payload: ThroughputEntryCreateInput) =>
|
||||
request<ThroughputEntry>('/api/throughput/entries', {
|
||||
@@ -389,22 +392,12 @@ export const api = {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
clientAccess: (fetcher?: ApiFetch) => cachedFetchJson<ClientAccessAccount[]>('/api/client-access', mockClientAccess, 'manager', fetcher),
|
||||
clientAccess: (fetcher?: ApiFetch) => cachedFetchJson<ClientAccessAccount[]>('/api/client-access', 'manager', fetcher),
|
||||
clientAccessExport: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<ClientAccessPowerBiExport>('/api/powerbi/client-access', mockClientAccessExport, 'manager', fetcher),
|
||||
dataQuality: (fetcher?: ApiFetch) => cachedFetchJson('/api/powerbi/data-quality-issues', [], 'client', fetcher),
|
||||
cachedFetchJson<ClientAccessPowerBiExport>('/api/powerbi/client-access', 'manager', fetcher),
|
||||
dataQuality: (fetcher?: ApiFetch) => cachedFetchJson('/api/powerbi/data-quality-issues', 'client', fetcher),
|
||||
dashboardSummary: (fetcher?: ApiFetch) =>
|
||||
cachedFetchJson<DashboardSummary>(
|
||||
'/api/dashboard/summary',
|
||||
{
|
||||
raw_materials: null,
|
||||
mixes: null,
|
||||
products: null,
|
||||
trend_seeds: { raw_material_cost_per_kg: [], mix_cost_per_kg: [], product_finished_delivered: [] }
|
||||
},
|
||||
'client',
|
||||
fetcher
|
||||
),
|
||||
cachedFetchJson<DashboardSummary>('/api/dashboard/summary', 'client', fetcher),
|
||||
clientLogin: (email: string, password: string) =>
|
||||
request<LoginResponse>('/api/auth/client/login', {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user