Initialize fresh project without Clerk

This commit is contained in:
2026-03-14 22:57:39 -03:00
commit 6898297935
401 changed files with 71631 additions and 0 deletions

View 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`);
};

View 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;

View 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',
},
});
};

View 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', {});
};

View 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}`);
}
};

View 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;
}[];
}

View 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}`);