v1.3 - client and admin scaffolding
This commit is contained in:
+114
-15
@@ -1,7 +1,24 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { mockCosts, mockMixes, mockProducts, mockRawMaterials, mockScenarios } from '$lib/mock';
|
||||
import { browser } from '$app/environment';
|
||||
import {
|
||||
mockClientAccess,
|
||||
mockClientAccessExport,
|
||||
mockCosts,
|
||||
mockMixes,
|
||||
mockProducts,
|
||||
mockRawMaterials,
|
||||
mockScenarios
|
||||
} from '$lib/mock';
|
||||
import type {
|
||||
ClientAccessAccount,
|
||||
ClientAccessPowerBiExport,
|
||||
ClientUserCreateInput,
|
||||
ClientUserUpdateInput,
|
||||
LoginResponse,
|
||||
Mix,
|
||||
MixCreateInput,
|
||||
MixIngredientUpdateInput,
|
||||
MixUpdateInput,
|
||||
Product,
|
||||
ProductCostBreakdown,
|
||||
RawMaterial,
|
||||
@@ -9,25 +26,55 @@ import type {
|
||||
RawMaterialPriceCreateInput,
|
||||
Scenario
|
||||
} from '$lib/types';
|
||||
import { getStoredAdminSession, getStoredClientSession } from '$lib/session';
|
||||
|
||||
const API_BASE_URL = env.PUBLIC_API_BASE_URL || 'http://localhost:8000';
|
||||
|
||||
async function fetchJson<T>(path: string, fallback: T): Promise<T> {
|
||||
type AuthMode = 'none' | 'client' | 'admin';
|
||||
|
||||
function getToken(auth: AuthMode) {
|
||||
if (!browser) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (auth === 'client') {
|
||||
return getStoredClientSession()?.token ?? null;
|
||||
}
|
||||
|
||||
if (auth === 'admin') {
|
||||
return getStoredAdminSession()?.token ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function fetchJson<T>(path: string, fallback: T, auth: AuthMode = 'none'): Promise<T> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${path}`);
|
||||
const token = getToken(auth);
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : undefined
|
||||
});
|
||||
if (!response.ok) {
|
||||
if (auth !== 'none') {
|
||||
throw new Error(response.statusText || 'Unauthorized');
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
return (await response.json()) as T;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (auth !== 'none') {
|
||||
throw error;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
async function request<T>(path: string, options: RequestInit): Promise<T> {
|
||||
async function request<T>(path: string, options: RequestInit, auth: AuthMode = 'none'): Promise<T> {
|
||||
const token = getToken(auth);
|
||||
const response = await fetch(`${API_BASE_URL}${path}`, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
...(options.headers ?? {})
|
||||
},
|
||||
...options
|
||||
@@ -50,25 +97,77 @@ async function request<T>(path: string, options: RequestInit): Promise<T> {
|
||||
}
|
||||
|
||||
export const api = {
|
||||
rawMaterials: () => fetchJson<RawMaterial[]>('/api/raw-materials', mockRawMaterials),
|
||||
mixes: () => fetchJson('/api/mixes', mockMixes),
|
||||
products: () => fetchJson<Product[]>('/api/products', mockProducts),
|
||||
productCosts: () => fetchJson<ProductCostBreakdown[]>('/api/powerbi/product-costs', mockCosts),
|
||||
scenarios: () => fetchJson<Scenario[]>('/api/scenarios', mockScenarios),
|
||||
dataQuality: () => fetchJson('/api/powerbi/data-quality-issues', []),
|
||||
login: (email: string, password: string) =>
|
||||
request<LoginResponse>('/api/auth/login', {
|
||||
rawMaterials: () => fetchJson<RawMaterial[]>('/api/raw-materials', mockRawMaterials, 'client'),
|
||||
mixes: () => fetchJson('/api/mixes', mockMixes, 'client'),
|
||||
mix: (mixId: number) => request<Mix>(`/api/mixes/${mixId}`, { method: 'GET' }, 'client'),
|
||||
products: () => fetchJson<Product[]>('/api/products', mockProducts, 'client'),
|
||||
productCosts: () => fetchJson<ProductCostBreakdown[]>('/api/powerbi/product-costs', mockCosts, 'client'),
|
||||
scenarios: () => fetchJson<Scenario[]>('/api/scenarios', mockScenarios, 'client'),
|
||||
clientAccess: () => fetchJson<ClientAccessAccount[]>('/api/client-access', mockClientAccess, 'admin'),
|
||||
clientAccessExport: () => fetchJson<ClientAccessPowerBiExport>('/api/powerbi/client-access', mockClientAccessExport, 'admin'),
|
||||
dataQuality: () => fetchJson('/api/powerbi/data-quality-issues', [], 'client'),
|
||||
clientLogin: (email: string, password: string) =>
|
||||
request<LoginResponse>('/api/auth/client/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email, password })
|
||||
}),
|
||||
adminLogin: (email: string, password: string) =>
|
||||
request<LoginResponse>('/api/auth/admin/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email, password })
|
||||
}),
|
||||
login: (email: string, password: string) =>
|
||||
request<LoginResponse>('/api/auth/client/login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email, password })
|
||||
}),
|
||||
createMix: (payload: MixCreateInput) =>
|
||||
request<Mix>('/api/mixes', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
updateMix: (mixId: number, payload: MixUpdateInput) =>
|
||||
request<Mix>(`/api/mixes/${mixId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
addMixIngredient: (mixId: number, payload: { raw_material_id: number; quantity_kg: number; notes?: string | null }) =>
|
||||
request<Mix>(`/api/mixes/${mixId}/ingredients`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
updateMixIngredient: (mixId: number, ingredientId: number, payload: MixIngredientUpdateInput) =>
|
||||
request<Mix>(`/api/mixes/${mixId}/ingredients/${ingredientId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'client'),
|
||||
deleteMixIngredient: (mixId: number, ingredientId: number) =>
|
||||
request<Mix>(`/api/mixes/${mixId}/ingredients/${ingredientId}`, {
|
||||
method: 'DELETE'
|
||||
}, 'client'),
|
||||
createRawMaterial: (payload: RawMaterialCreateInput) =>
|
||||
request<RawMaterial>('/api/raw-materials', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
}),
|
||||
}, 'client'),
|
||||
addRawMaterialPrice: (rawMaterialId: number, payload: RawMaterialPriceCreateInput) =>
|
||||
request(`/api/raw-materials/${rawMaterialId}/prices`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
}, 'client'),
|
||||
createClientUser: (payload: ClientUserCreateInput) =>
|
||||
request<ClientAccessAccount>('/api/client-access/users', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'admin'),
|
||||
updateClientUser: (userId: number, payload: ClientUserUpdateInput) =>
|
||||
request<ClientAccessAccount>(`/api/client-access/users/${userId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'admin'),
|
||||
updateClientFeature: (featureId: number, payload: { enabled: boolean }) =>
|
||||
request<ClientAccessAccount>(`/api/client-access/features/${featureId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(payload)
|
||||
}, 'admin')
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user