Initialize fresh project without Clerk
This commit is contained in:
25
src/client/services/analysisService.ts
Normal file
25
src/client/services/analysisService.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import api from './api';
|
||||
|
||||
export interface AnalysisResult {
|
||||
pieceDescription: string;
|
||||
schemeName: string;
|
||||
schemeType?: string;
|
||||
realYield: number;
|
||||
theoreticalYield: number;
|
||||
yieldVariance: number;
|
||||
yieldStatus: 'approved' | 'warning' | 'critical';
|
||||
diluentUsed: number;
|
||||
volumeUsed: number;
|
||||
realDilution: number;
|
||||
targetDilution: number;
|
||||
dilutionStatus: 'approved' | 'warning' | 'critical';
|
||||
realDFT: number;
|
||||
minDFT: number;
|
||||
maxDFT: number;
|
||||
dftStatus: 'approved' | 'warning' | 'critical';
|
||||
notes: string[];
|
||||
}
|
||||
|
||||
export const getProjectAnalysis = async (projectId: string) => {
|
||||
return api.get<AnalysisResult[]>(`/projects/${projectId}/analysis`);
|
||||
};
|
||||
81
src/client/services/api.ts
Normal file
81
src/client/services/api.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// API service configuration v1.4 - with auth and error interceptors
|
||||
import axios from 'axios';
|
||||
import { triggerGuestWarning } from '../utils/toastHandler';
|
||||
|
||||
export const getBaseUrl = () => {
|
||||
// Priority: Env var -> Relative path (handled by Vite proxy in dev, or Nginx/Vercel in prod)
|
||||
if (import.meta.env.VITE_API_URL) {
|
||||
return import.meta.env.VITE_API_URL;
|
||||
}
|
||||
return '/api';
|
||||
};
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: getBaseUrl(),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
let currentToken: string | null = null;
|
||||
let currentOrgId: string | null = null;
|
||||
let currentOrgName: string | null = null;
|
||||
|
||||
// Function to set the JWT token
|
||||
export const setApiToken = (token: string | null) => {
|
||||
currentToken = token;
|
||||
};
|
||||
|
||||
// Function to set the organization ID and Name (called from Layout/Context)
|
||||
export const setApiOrgData = (orgId: string | null, orgName: string | null = null) => {
|
||||
currentOrgId = orgId;
|
||||
currentOrgName = orgName;
|
||||
};
|
||||
|
||||
// Legacy support
|
||||
export const setApiOrgId = (orgId: string | null) => {
|
||||
setApiOrgData(orgId, null);
|
||||
};
|
||||
|
||||
// Alias for consistency
|
||||
export const setApiOrganizationId = setApiOrgId;
|
||||
|
||||
// Request interceptor to add clerk user ID and Org ID headers
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
console.log(`[API Request] ${config.method?.toUpperCase()} ${config.url}`, {
|
||||
orgId: currentOrgId
|
||||
});
|
||||
if (currentToken) {
|
||||
config.headers['Authorization'] = `Bearer ${currentToken}`;
|
||||
}
|
||||
if (currentOrgId) {
|
||||
config.headers['x-organization-id'] = currentOrgId;
|
||||
}
|
||||
if (currentOrgName) {
|
||||
// Encode to handle special characters
|
||||
config.headers['x-organization-name'] = encodeURIComponent(currentOrgName);
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor to handle 403 errors (guest access denied)
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response?.status === 403) {
|
||||
// Check if it's a guest permission error
|
||||
const errorMessage = error.response?.data?.error || '';
|
||||
if (errorMessage.includes('Convidados') || errorMessage.includes('guest') || errorMessage.includes('permissão')) {
|
||||
triggerGuestWarning();
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
37
src/client/services/dataSheetService.ts
Normal file
37
src/client/services/dataSheetService.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import api, { getBaseUrl } from './api';
|
||||
export { getBaseUrl };
|
||||
import type { TechnicalDataSheet } from '../types';
|
||||
|
||||
export const getDataSheets = async () => {
|
||||
return api.get<TechnicalDataSheet[]>('/datasheets');
|
||||
};
|
||||
|
||||
export const createDataSheet = async (formData: FormData) => {
|
||||
return api.post<TechnicalDataSheet>('/datasheets', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const updateDataSheet = async (id: string, formData: FormData) => {
|
||||
return api.put<TechnicalDataSheet>(`/datasheets/${id}`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteDataSheet = async (id: string) => {
|
||||
return api.delete(`/datasheets/${id}`);
|
||||
};
|
||||
|
||||
export const extractDataSheet = async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return api.post('/datasheets/extract', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
};
|
||||
22
src/client/services/geometryTypeService.ts
Normal file
22
src/client/services/geometryTypeService.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import api from './api';
|
||||
import type { GeometryType } from '../types';
|
||||
|
||||
export const getAllTypes = async () => {
|
||||
return api.get<GeometryType[]>('/geometry-types');
|
||||
};
|
||||
|
||||
export const createType = async (data: { name: string; efficiencyLoss: number }) => {
|
||||
return api.post<GeometryType>('/geometry-types', data);
|
||||
};
|
||||
|
||||
export const updateType = async (id: string, data: { name: string; efficiencyLoss: number }) => {
|
||||
return api.put<GeometryType>(`/geometry-types/${id}`, data);
|
||||
};
|
||||
|
||||
export const deleteType = async (id: string) => {
|
||||
return api.delete(`/geometry-types/${id}`);
|
||||
};
|
||||
|
||||
export const restoreDefaults = async () => {
|
||||
return api.post<GeometryType[]>('/geometry-types/restore', {});
|
||||
};
|
||||
88
src/client/services/stockService.ts
Normal file
88
src/client/services/stockService.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import api from './api';
|
||||
|
||||
export interface StockItem {
|
||||
id?: string;
|
||||
_id?: string;
|
||||
organizationId?: string;
|
||||
dataSheetId: string | any; // Can be object when populated
|
||||
rrNumber: string;
|
||||
batchNumber: string;
|
||||
color?: string;
|
||||
invoiceNumber?: string;
|
||||
receivedBy?: string;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
minStock?: number;
|
||||
expirationDate?: string;
|
||||
entryDate?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface StockMovement {
|
||||
id?: string;
|
||||
_id?: string;
|
||||
stockItemId: string;
|
||||
type: 'ENTRY' | 'ADJUSTMENT' | 'CONSUMPTION';
|
||||
quantity: number;
|
||||
date: string;
|
||||
responsible: string;
|
||||
reason?: string;
|
||||
requester?: string;
|
||||
notes?: string;
|
||||
movementNumber?: number;
|
||||
}
|
||||
|
||||
export const stockService = {
|
||||
getAll: async (dataSheetId?: string) => {
|
||||
const response = await api.get('/stock', { params: { dataSheetId } });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getById: async (id: string) => {
|
||||
const response = await api.get(`/stock/${id}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getMovements: async (id: string) => {
|
||||
const response = await api.get(`/stock/${id}/movements`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getAuditLogs: async (id: string) => {
|
||||
const response = await api.get(`/stock/${id}/logs`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
create: async (data: Partial<StockItem>) => {
|
||||
const response = await api.post('/stock', data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
update: async (id: string, data: Partial<StockItem>) => {
|
||||
const response = await api.put(`/stock/${id}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
adjust: async (id: string, data: { quantityDelta: number; reason: string }) => {
|
||||
const response = await api.post(`/stock/${id}/adjust`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
consume: async (id: string, data: { quantityConsumed: number; requester: string; date?: string }) => {
|
||||
const response = await api.post(`/stock/${id}/consume`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
delete: async (id: string) => {
|
||||
await api.delete(`/stock/${id}`);
|
||||
},
|
||||
|
||||
updateMovement: async (id: string, data: Partial<StockMovement>) => {
|
||||
const response = await api.put(`/stock/movements/${id}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
deleteMovement: async (id: string) => {
|
||||
await api.delete(`/stock/movements/${id}`);
|
||||
}
|
||||
};
|
||||
75
src/client/services/systemSettingsService.ts
Normal file
75
src/client/services/systemSettingsService.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import api from './api';
|
||||
|
||||
export interface SystemSettings {
|
||||
settingsId: string;
|
||||
appName: string;
|
||||
appSubtitle: string;
|
||||
appLogoUrl?: string;
|
||||
updatedBy?: string;
|
||||
}
|
||||
|
||||
export const systemSettingsService = {
|
||||
getSettings: async (): Promise<SystemSettings> => {
|
||||
const response = await api.get('/system-settings');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
updateSettings: async (settings: Partial<SystemSettings>): Promise<SystemSettings> => {
|
||||
// Axios interceptors in api.ts automatically handle x-auth-user-id and x-organization-id headers
|
||||
const response = await api.put('/system-settings', settings);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
uploadLogo: async (file: File): Promise<string> => {
|
||||
const formData = new FormData();
|
||||
formData.append('logo', file);
|
||||
|
||||
const response = await api.post('/system-settings/logo', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
return response.data.url;
|
||||
},
|
||||
|
||||
getGlobalUsers: async (): Promise<GlobalUser[]> => {
|
||||
const response = await api.get('/system-settings/users');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getGlobalOrganizations: async (): Promise<GlobalOrganization[]> => {
|
||||
const response = await api.get('/system-settings/organizations');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
toggleOrganizationBan: async (organizationId: string, isBanned: boolean): Promise<any> => {
|
||||
const response = await api.post('/system-settings/organizations/ban', { organizationId, isBanned });
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
export interface GlobalUser {
|
||||
_id: string;
|
||||
externalId: string;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
isBanned: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface GlobalOrganization {
|
||||
_id: string; // organizationId
|
||||
memberCount: number;
|
||||
lastActive: string;
|
||||
isBanned: boolean;
|
||||
name?: string; // Added
|
||||
members: {
|
||||
name: string;
|
||||
email: string;
|
||||
role: 'admin' | 'user' | 'guest';
|
||||
userId: string;
|
||||
isBanned: boolean;
|
||||
}[];
|
||||
}
|
||||
9
src/client/services/yieldStudyService.ts
Normal file
9
src/client/services/yieldStudyService.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import api from './api';
|
||||
import type { YieldStudy } from '../types';
|
||||
|
||||
const BASE_URL = '/yield-studies';
|
||||
|
||||
export const getStudies = () => api.get<YieldStudy[]>(BASE_URL);
|
||||
export const createStudy = (data: Partial<YieldStudy>) => api.post<YieldStudy>(BASE_URL, data);
|
||||
export const updateStudy = (id: string, data: Partial<YieldStudy>) => api.put<YieldStudy>(`${BASE_URL}/${id}`, data);
|
||||
export const deleteStudy = (id: string) => api.delete(`${BASE_URL}/${id}`);
|
||||
Reference in New Issue
Block a user