diff --git a/frontend-web/src/api/client.js b/frontend-web/src/api/client.js new file mode 100644 index 00000000..e63a00f7 --- /dev/null +++ b/frontend-web/src/api/client.js @@ -0,0 +1,22 @@ +import axios from "axios"; +import { auth } from "../firebase"; + +const apiClient = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL, // You will need to add this to your .env file +}); + +apiClient.interceptors.request.use( + async (config) => { + const user = auth.currentUser; + if (user) { + const token = await user.getIdToken(); + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +export default apiClient; \ No newline at end of file diff --git a/frontend-web/src/api/krowSDK.js b/frontend-web/src/api/krowSDK.js new file mode 100644 index 00000000..45d6c64f --- /dev/null +++ b/frontend-web/src/api/krowSDK.js @@ -0,0 +1,466 @@ +import apiClient from './client'; +import { auth, dataConnect } from '../firebase'; +import { signOut } from 'firebase/auth'; + +import * as dcSdk from '@dataconnect/generated'; // listEvents, createEvent, etc. + +// --- Auth Module --- +const authModule = { + /** + * Fetches the currently authenticated user's profile from the backend. + * @returns {Promise} The user profile. + */ + me: async () => { + const { data } = await apiClient.get('/auth/me'); + return data; + }, + + /** + * Logs the user out. + * @param {string} [redirectUrl] - Optional URL to redirect to after logout. + */ + logout: async (redirectUrl) => { + await signOut(auth); + if (redirectUrl) { + window.location.href = redirectUrl; + } + }, + + /** + * Checks if a user is currently authenticated. + * @returns {boolean} True if a user is authenticated. + */ + isAuthenticated: () => { + return !!auth.currentUser; + }, +}; + +// --- Core Integrations Module --- +const coreIntegrationsModule = { + /** + * Sends an email. + * @param {object} params - { to, subject, body } + * @returns {Promise} API response. + */ + SendEmail: async (params) => { + const { data } = await apiClient.post('/sendEmail', params); + return data; + }, + + /** + * Invokes a large language model. + * @param {object} params - { prompt, response_json_schema, file_urls } + * @returns {Promise} API response. + */ + InvokeLLM: async (params) => { + const { data } = await apiClient.post('/invokeLLM', params); + return data; + }, + + /** + * Uploads a public file. + * @param {File} file - The file to upload. + * @returns {Promise} API response with file_url. + */ + UploadFile: async ({ file }) => { + const formData = new FormData(); + formData.append('file', file); + const { data } = await apiClient.post('/uploadFile', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return data; + }, + + /** + * Uploads a private file. + * @param {File} file - The file to upload. + * @returns {Promise} API response with file_uri. + */ + UploadPrivateFile: async ({ file }) => { + const formData = new FormData(); + formData.append('file', file); + const { data } = await apiClient.post('/uploadPrivateFile', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return data; + }, + + /** + * Creates a temporary signed URL for a private file. + * @param {object} params - { file_uri, expires_in } + * @returns {Promise} API response with signed_url. + */ + CreateFileSignedUrl: async (params) => { + const { data } = await apiClient.post('/createSignedUrl', params); + return data; + }, +}; + +const dataconnectEntityConfig = { + User: { + list: 'listUsers', + get: 'getUserById', + create: 'createUser', + update: 'updateUser', + delete: 'deleteUser', + filter: 'filterUsers', + }, + Event: { + list: 'listEvents', + create: 'createEvent', + get: 'getEventById', + update: 'updateEvent', + delete: 'deleteEvent', + filter: 'filterEvents', + }, + + Staff: { + list: 'listStaff', + create: 'createStaff', + get: 'getStaffById', + update: 'updateStaff', + delete: 'deleteStaff', + filter: 'filterStaff', + }, + + Vendor: { + list: 'listVendor', + get: 'getVendorById', + create: 'createVendor', + update: 'updateVendor', + delete: 'deleteVendor', + filter: 'filterVendors', + }, + + VendorRate: { + list: 'listVendorRate', + get: 'getVendorRateById', + create: 'createVendorRate', + update: 'updateVendorRate', + delete: 'deleteVendorRate', + filter: 'filterVendorRates', + }, + + VendorDefaultSetting:{ + list: 'listVendorDefaultSettings', + get: 'getVendorDefaultSettingById', + create: 'createVendorDefaultSetting', + update: 'updateVendorDefaultSetting', + delete: 'deleteVendorDefaultSetting', + filter: 'filterVendorDefaultSettings', + }, + + Invoice:{ + list: 'listInvoice', + get: 'getInvoiceById', + create: 'createInvoice', + update: 'updateInvoice', + delete: 'deleteInvoice', + filter: 'filterInvoices', + + }, + + Business:{ + list: 'listBusiness', + get: 'getBusinessById', + create: 'createBusiness', + update: 'updateBusiness', + delete: 'deleteBusiness', + filter: 'filterBusiness', + }, + + Certification:{ + list: 'listCertification', + get: 'getCertificationById', + create: 'createCertification', + update: 'updateCertification', + delete: 'deleteCertification', + filter: 'filterCertification', + }, + + Team:{ + list: 'listTeam', + get: 'getTeamById', + create: 'createTeam', + update: 'updateTeam', + delete: 'deleteTeam', + filter: 'filterTeam', + }, + + TeamMember: { + list: 'listTeamMember', + get: 'getTeamMemberById', + create: 'createTeamMember', + update: 'updateTeamMember', + delete: 'deleteTeamMember', + filter: 'filterTeamMember', + }, + + TeamHub: { + list: 'listTeamHub', + get: 'getTeamHubById', + create: 'createTeamHub', + update: 'updateTeamHub', + delete: 'deleteTeamHub', + filter: 'filterTeamHub', + }, + + TeamMemberInvite: { + list: 'listTeamMemberInvite', + get: 'getTeamMemberInviteById', + create: 'createTeamMemberInvite', + update: 'updateTeamMemberInvite', + delete: 'deleteTeamMemberInvite', + filter: 'filterTeamMemberInvite', + }, + + Conversation:{ + list: 'listConversation', + get: 'getConversationById', + create: 'createConversation', + update: 'updateConversation', + delete: 'deleteConversation', + filter: 'filterConversation', + }, + + Message:{ + list: 'listMessage', + get: 'getMessageById', + create: 'createMessage', + update: 'updateMessage', + delete: 'deleteMessage', + filter: 'filterMessage', + }, + + ActivityLog:{ + list: 'listActivityLog', + get: 'getActivityLogById', + create: 'createActivityLog', + update: 'updateActivityLog', + delete: 'deleteActivityLog', + filter: 'filterActivityLog', + }, + + Enterprise:{ + list: 'listEnterprise', + get: 'getEnterpriseById', + create: 'createEnterprise', + update: 'updateEnterprise', + delete: 'deleteEnterprise', + filter: 'filterEnterprise', + }, + + Sector:{ + + }, + + Partner:{ + + }, + + Order:{ + + }, + + Shift:{ + + } + +}; + +// Helper for methods not implemented +const notImplemented = (entityName, method) => async () => { + throw new Error(`${entityName}.${method} is not implemented yet for Data Connect`); +}; + +// --- Entities Module ( Data Connect, without REST Base44) --- +const entitiesModule = {}; + +Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => { + entitiesModule[entityName] = { + + get: notImplemented(entityName, 'get'), + update: notImplemented(entityName, 'update'), + delete: notImplemented(entityName, 'delete'), + filter: notImplemented(entityName, 'filter'), + list: notImplemented(entityName, 'list'), + create: notImplemented(entityName, 'create'), + + // list + ...(ops.list && { + list: async (params) => { + const fn = dcSdk[ops.list]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.list}" not found for entity "${entityName}".` + ); + } + + return fn(dataConnect); + }, + }), + + // create + ...(ops.create && { + create: async (params) => { + const fn = dcSdk[ops.create]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.create}" not found for entity "${entityName}".` + ); + } + + const { data } = params ?? {}; + if (!data) { + throw new Error( + `${entityName}.create expects a payload like { data: { ...fields } }` + ); + } + + return fn(dataConnect, data); + }, + }), + + //get + ...(ops.get && { + get: async (params) => { + const fn = dcSdk[ops.get]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.get}" not found for entity "${entityName}".` + ); + } + + if (!params || typeof params !== 'object') { + throw new Error(`${entityName}.get expects an object of variables (e.g. { id })`); + } + + return fn(dataConnect, params); + }, + }), + + //update + ...(ops.update && { + update: async (params) => { + const fn = dcSdk[ops.update]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.update}" not found for entity "${entityName}".` + ); + } + + if (!params || typeof params !== 'object') { + throw new Error( + `${entityName}.update expects an object of variables matching the GraphQL mutation` + ); + } + + const { id, data } = params; + + if (!id) { + throw new Error(`${entityName}.update requires an "id" field`); + } + + if (!data || typeof data !== 'object') { + throw new Error( + `${entityName}.update requires a "data" object with the fields to update` + ); + } + + const vars = { id, ...data }; + + return fn(dataConnect, vars); + + }, + }), + + + // delete + ...(ops.delete && { + delete: async (params) => { + const fn = dcSdk[ops.delete]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.delete}" not found for entity "${entityName}".` + ); + } + + if (!params || typeof params !== 'object') { + throw new Error( + `${entityName}.delete expects an object like { id }` + ); + } + + const { id } = params; + + if (!id) { + throw new Error(`${entityName}.delete requires an "id" field`); + } + + // Data Connect solo espera { id } como variables + return fn(dataConnect, { id }); + }, + }), + + + // filter + ...(ops.filter && { + filter: async (params) => { + const fn = dcSdk[ops.filter]; + if (typeof fn !== 'function') { + throw new Error( + `Data Connect operation "${ops.filter}" not found for entity "${entityName}".` + ); + } + + if (!params) { + if (ops.list) {//if no params, call to list() + const listFn = dcSdk[ops.list]; + if (typeof listFn !== 'function') { + throw new Error( + `Data Connect operation "${ops.list}" not found for entity "${entityName}".` + ); + } + return listFn(dataConnect); + } + throw new Error(`${entityName}.filter expects params or a list operation`); + } + const rawFilters = params.filters ?? params; + const variables = {}; + + for (const [key, value] of Object.entries(rawFilters)) {//cleaning undefined/null/'' values + if (value !== undefined && value !== null && value !== '') { + variables[key] = value; + } + } + + // if no valid filters, call to list() + if (Object.keys(variables).length === 0) { + if (ops.list) { + const listFn = dcSdk[ops.list]; + if (typeof listFn !== 'function') { + throw new Error( + `Data Connect operation "${ops.list}" not found for entity "${entityName}".` + ); + } + return listFn(dataConnect); + } + throw new Error(`${entityName}.filter received no valid filters and no list operation`); + } + + return fn(dataConnect, variables); + }, + }), + + }; +}); + + +// --- Main SDK Export --- +export const krowSDK = { + auth: authModule, + integrations: { + Core: coreIntegrationsModule, + }, + entities: entitiesModule, +};