From a5064f21722a31685d09af8b5ff404912e6e31ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoni=20Nu=C3=B1ez=20Romeu?= Date: Mon, 6 Jul 2026 11:00:44 +0200 Subject: [PATCH] feat: add API client and services for medicines, pharmacies, auth --- frontend-mobile/constants/config.ts | 19 ++++++++++++ frontend-mobile/services/api.ts | 36 +++++++++++++++++++++ frontend-mobile/services/auth.ts | 43 ++++++++++++++++++++++++++ frontend-mobile/services/medicines.ts | 17 ++++++++++ frontend-mobile/services/pharmacies.ts | 17 ++++++++++ 5 files changed, 132 insertions(+) create mode 100644 frontend-mobile/constants/config.ts create mode 100644 frontend-mobile/services/api.ts create mode 100644 frontend-mobile/services/auth.ts create mode 100644 frontend-mobile/services/medicines.ts create mode 100644 frontend-mobile/services/pharmacies.ts diff --git a/frontend-mobile/constants/config.ts b/frontend-mobile/constants/config.ts new file mode 100644 index 0000000..92299fe --- /dev/null +++ b/frontend-mobile/constants/config.ts @@ -0,0 +1,19 @@ +import Constants from 'expo-constants'; + +const ENV = { + development: { + API_BASE_URL: 'http://localhost:3001/api', + }, + production: { + API_BASE_URL: 'https://your-production-api.com/api', + }, +}; + +const environment = Constants.expoConfig?.extra?.environment || (__DEV__ ? 'development' : 'production'); + +export const config = { + API_BASE_URL: ENV[environment as keyof typeof ENV]?.API_BASE_URL || ENV.development.API_BASE_URL, + SEARCH_DEBOUNCE_MS: 300, + CACHE_STALE_TIME: 5 * 60 * 1000, // 5 minutes + CACHE_CACHE_TIME: 30 * 60 * 1000, // 30 minutes +}; diff --git a/frontend-mobile/services/api.ts b/frontend-mobile/services/api.ts new file mode 100644 index 0000000..592f0fb --- /dev/null +++ b/frontend-mobile/services/api.ts @@ -0,0 +1,36 @@ +import axios from 'axios'; +import * as SecureStore from 'expo-secure-store'; +import { config } from '../constants/config'; + +export const api = axios.create({ + baseURL: config.API_BASE_URL, + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +// Request interceptor - add auth token +api.interceptors.request.use( + async (axiosConfig) => { + const token = await SecureStore.getItemAsync('auth_token'); + if (token) { + axiosConfig.headers.Authorization = `Bearer ${token}`; + } + return axiosConfig; + }, + (error) => Promise.reject(error) +); + +// Response interceptor - handle errors +api.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.status === 401) { + await SecureStore.deleteItemAsync('auth_token'); + } + return Promise.reject(error); + } +); + +export default api; diff --git a/frontend-mobile/services/auth.ts b/frontend-mobile/services/auth.ts new file mode 100644 index 0000000..0ed8d4b --- /dev/null +++ b/frontend-mobile/services/auth.ts @@ -0,0 +1,43 @@ +import api from './api'; +import * as SecureStore from 'expo-secure-store'; +import { AuthResponse, User } from '../types'; + +export async function login(username: string, password: string): Promise { + const response = await api.post('/auth/login', { username, password }); + const { user, token } = response.data; + + await SecureStore.setItemAsync('auth_token', token); + await SecureStore.setItemAsync('user', JSON.stringify(user)); + + return response.data; +} + +export async function logout(): Promise { + await api.post('/auth/logout'); + await SecureStore.deleteItemAsync('auth_token'); + await SecureStore.deleteItemAsync('user'); +} + +export async function checkAuth(): Promise { + try { + const response = await api.get('/auth/check'); + return response.data.user; + } catch { + return null; + } +} + +export async function getStoredUser(): Promise { + const userStr = await SecureStore.getItemAsync('user'); + return userStr ? JSON.parse(userStr) : null; +} + +export async function register(username: string, password: string): Promise { + const response = await api.post('/auth/register', { username, password }); + const { user, token } = response.data; + + await SecureStore.setItemAsync('auth_token', token); + await SecureStore.setItemAsync('user', JSON.stringify(user)); + + return response.data; +} diff --git a/frontend-mobile/services/medicines.ts b/frontend-mobile/services/medicines.ts new file mode 100644 index 0000000..5437984 --- /dev/null +++ b/frontend-mobile/services/medicines.ts @@ -0,0 +1,17 @@ +import api from './api'; +import { Medicine, PharmacyMedicine } from '../types'; + +export async function searchMedicines(query: string): Promise { + const response = await api.get(`/medicines/search?q=${encodeURIComponent(query)}`); + return response.data; +} + +export async function getMedicine(nregistro: string): Promise { + const response = await api.get(`/medicines/${nregistro}`); + return response.data; +} + +export async function getMedicinePharmacies(nregistro: string): Promise { + const response = await api.get(`/medicines/${nregistro}/pharmacies`); + return response.data; +} diff --git a/frontend-mobile/services/pharmacies.ts b/frontend-mobile/services/pharmacies.ts new file mode 100644 index 0000000..6b90448 --- /dev/null +++ b/frontend-mobile/services/pharmacies.ts @@ -0,0 +1,17 @@ +import api from './api'; +import { Pharmacy } from '../types'; + +export async function getPharmacies(): Promise { + const response = await api.get('/pharmacies'); + return response.data; +} + +export async function getPharmacy(id: number): Promise { + const response = await api.get(`/pharmacies/${id}`); + return response.data; +} + +export async function getPharmacyMedicines(id: number): Promise { + const response = await api.get(`/pharmacies/${id}/medicines`); + return response.data; +}