Start integrating the onboarding flow with backend 1
This commit is contained in:
@@ -86,7 +86,13 @@ class AuthService {
|
||||
|
||||
// Authentication endpoints
|
||||
async register(userData: UserRegistration): Promise<ApiResponse<TokenResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}/register`, userData);
|
||||
const response = await apiClient.post<TokenResponse>(`${this.baseUrl}/register`, userData);
|
||||
|
||||
if (response.success && response.data) {
|
||||
this.handleSuccessfulAuth(response.data);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async login(credentials: UserLogin): Promise<ApiResponse<TokenResponse>> {
|
||||
@@ -164,54 +170,70 @@ class AuthService {
|
||||
return apiClient.post(`${this.baseUrl}/verify-email/confirm`, { token });
|
||||
}
|
||||
|
||||
// Local auth state management
|
||||
// Local auth state management - Now handled by Zustand store
|
||||
private handleSuccessfulAuth(tokenData: TokenResponse) {
|
||||
localStorage.setItem('access_token', tokenData.access_token);
|
||||
|
||||
if (tokenData.refresh_token) {
|
||||
localStorage.setItem('refresh_token', tokenData.refresh_token);
|
||||
}
|
||||
|
||||
if (tokenData.user) {
|
||||
localStorage.setItem('user_data', JSON.stringify(tokenData.user));
|
||||
|
||||
if (tokenData.user.tenant_id) {
|
||||
localStorage.setItem('tenant_id', tokenData.user.tenant_id);
|
||||
apiClient.setTenantId(tokenData.user.tenant_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Set auth token for API client
|
||||
apiClient.setAuthToken(tokenData.access_token);
|
||||
|
||||
// Set tenant ID for API client if available
|
||||
if (tokenData.user?.tenant_id) {
|
||||
apiClient.setTenantId(tokenData.user.tenant_id);
|
||||
}
|
||||
}
|
||||
|
||||
private clearAuthData() {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
localStorage.removeItem('user_data');
|
||||
localStorage.removeItem('tenant_id');
|
||||
// Clear API client tokens
|
||||
apiClient.removeAuthToken();
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
// Utility methods - Now get data from Zustand store
|
||||
isAuthenticated(): boolean {
|
||||
return !!localStorage.getItem('access_token');
|
||||
const authStorage = localStorage.getItem('auth-storage');
|
||||
if (!authStorage) return false;
|
||||
try {
|
||||
const { state } = JSON.parse(authStorage);
|
||||
return state?.isAuthenticated || false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentUserData(): UserData | null {
|
||||
const userData = localStorage.getItem('user_data');
|
||||
return userData ? JSON.parse(userData) : null;
|
||||
const authStorage = localStorage.getItem('auth-storage');
|
||||
if (!authStorage) return null;
|
||||
try {
|
||||
const { state } = JSON.parse(authStorage);
|
||||
return state?.user || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getAccessToken(): string | null {
|
||||
return localStorage.getItem('access_token');
|
||||
const authStorage = localStorage.getItem('auth-storage');
|
||||
if (!authStorage) return null;
|
||||
try {
|
||||
const { state } = JSON.parse(authStorage);
|
||||
return state?.token || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getRefreshToken(): string | null {
|
||||
return localStorage.getItem('refresh_token');
|
||||
const authStorage = localStorage.getItem('auth-storage');
|
||||
if (!authStorage) return null;
|
||||
try {
|
||||
const { state } = JSON.parse(authStorage);
|
||||
return state?.refreshToken || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getTenantId(): string | null {
|
||||
return localStorage.getItem('tenant_id');
|
||||
const userData = this.getCurrentUserData();
|
||||
return userData?.tenant_id || null;
|
||||
}
|
||||
|
||||
// Check if token is expired (basic check)
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
import axios, { AxiosInstance, AxiosResponse, AxiosError, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
// Utility functions to access auth and tenant store data from localStorage
|
||||
const getAuthData = () => {
|
||||
const authStorage = localStorage.getItem('auth-storage');
|
||||
if (!authStorage) return null;
|
||||
try {
|
||||
const { state } = JSON.parse(authStorage);
|
||||
return state;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getTenantData = () => {
|
||||
const tenantStorage = localStorage.getItem('tenant-storage');
|
||||
if (!tenantStorage) return null;
|
||||
try {
|
||||
const { state } = JSON.parse(tenantStorage);
|
||||
return state;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const clearAuthData = () => {
|
||||
localStorage.removeItem('auth-storage');
|
||||
};
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
data: T;
|
||||
success: boolean;
|
||||
@@ -41,12 +68,15 @@ class ApiClient {
|
||||
// Request interceptor - add auth token and tenant ID
|
||||
this.axiosInstance.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
const authData = getAuthData();
|
||||
if (authData?.token) {
|
||||
config.headers.Authorization = `Bearer ${authData.token}`;
|
||||
}
|
||||
|
||||
const tenantId = localStorage.getItem('tenant_id');
|
||||
// Get tenant ID from tenant store (priority) or fallback to user's tenant_id
|
||||
const tenantData = getTenantData();
|
||||
const tenantId = tenantData?.currentTenant?.id || authData?.user?.tenant_id;
|
||||
|
||||
if (tenantId) {
|
||||
config.headers['X-Tenant-ID'] = tenantId;
|
||||
}
|
||||
@@ -67,8 +97,8 @@ class ApiClient {
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refresh_token');
|
||||
if (refreshToken) {
|
||||
const authData = getAuthData();
|
||||
if (authData?.refreshToken) {
|
||||
const newToken = await this.refreshToken();
|
||||
originalRequest.headers.Authorization = `Bearer ${newToken}`;
|
||||
return this.axiosInstance(originalRequest);
|
||||
@@ -113,29 +143,34 @@ class ApiClient {
|
||||
}
|
||||
|
||||
private async performTokenRefresh(): Promise<string> {
|
||||
const refreshToken = localStorage.getItem('refresh_token');
|
||||
if (!refreshToken) {
|
||||
const authData = getAuthData();
|
||||
if (!authData?.refreshToken) {
|
||||
throw new Error('No refresh token available');
|
||||
}
|
||||
|
||||
const response = await axios.post(`${API_BASE_URL}/auth/refresh`, {
|
||||
refresh_token: refreshToken,
|
||||
refresh_token: authData.refreshToken,
|
||||
});
|
||||
|
||||
const { access_token, refresh_token } = response.data;
|
||||
localStorage.setItem('access_token', access_token);
|
||||
if (refresh_token) {
|
||||
localStorage.setItem('refresh_token', refresh_token);
|
||||
}
|
||||
|
||||
// Update the Zustand store by modifying the auth-storage directly
|
||||
const newAuthData = {
|
||||
...authData,
|
||||
token: access_token,
|
||||
refreshToken: refresh_token || authData.refreshToken
|
||||
};
|
||||
|
||||
localStorage.setItem('auth-storage', JSON.stringify({
|
||||
state: newAuthData,
|
||||
version: 0
|
||||
}));
|
||||
|
||||
return access_token;
|
||||
}
|
||||
|
||||
private handleAuthFailure() {
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('refresh_token');
|
||||
localStorage.removeItem('user_data');
|
||||
localStorage.removeItem('tenant_id');
|
||||
clearAuthData();
|
||||
|
||||
// Redirect to login
|
||||
window.location.href = '/login';
|
||||
@@ -197,19 +232,16 @@ class ApiClient {
|
||||
};
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
// Utility methods - Now work with Zustand store
|
||||
setAuthToken(token: string) {
|
||||
localStorage.setItem('access_token', token);
|
||||
this.axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
removeAuthToken() {
|
||||
localStorage.removeItem('access_token');
|
||||
delete this.axiosInstance.defaults.headers.common['Authorization'];
|
||||
}
|
||||
|
||||
setTenantId(tenantId: string) {
|
||||
localStorage.setItem('tenant_id', tenantId);
|
||||
this.axiosInstance.defaults.headers.common['X-Tenant-ID'] = tenantId;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { apiClient, ApiResponse } from './client';
|
||||
|
||||
// Request/Response Types based on backend schemas
|
||||
// Request/Response Types based on backend schemas - UPDATED TO MATCH BACKEND
|
||||
export interface BakeryRegistration {
|
||||
name: string;
|
||||
address: string;
|
||||
@@ -101,7 +101,7 @@ class TenantService {
|
||||
|
||||
// Tenant CRUD operations
|
||||
async createTenant(tenantData: BakeryRegistration): Promise<ApiResponse<TenantResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}`, tenantData);
|
||||
return apiClient.post(`${this.baseUrl}/register`, tenantData);
|
||||
}
|
||||
|
||||
async getTenant(tenantId: string): Promise<ApiResponse<TenantResponse>> {
|
||||
@@ -151,15 +151,31 @@ class TenantService {
|
||||
}
|
||||
|
||||
async switchTenant(tenantId: string): Promise<ApiResponse<{ message: string; tenant: TenantResponse }>> {
|
||||
const response = await apiClient.post(`${this.baseUrl}/${tenantId}/switch`);
|
||||
|
||||
if (response.success && response.data?.tenant) {
|
||||
// Update local tenant context
|
||||
localStorage.setItem('tenant_id', tenantId);
|
||||
apiClient.setTenantId(tenantId);
|
||||
// Frontend-only tenant switching since backend doesn't have this endpoint
|
||||
// We'll simulate the response and update the tenant store directly
|
||||
try {
|
||||
const tenant = await this.getTenant(tenantId);
|
||||
if (tenant.success) {
|
||||
// Update API client tenant context
|
||||
apiClient.setTenantId(tenantId);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: 'Tenant switched successfully',
|
||||
tenant: tenant.data
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new Error('Tenant not found');
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
data: null,
|
||||
error: error instanceof Error ? error.message : 'Failed to switch tenant'
|
||||
};
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Member management
|
||||
@@ -228,33 +244,33 @@ class TenantService {
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
async getUserTenants(): Promise<ApiResponse<TenantResponse[]>> {
|
||||
return apiClient.get(`${this.baseUrl}/my-tenants`);
|
||||
async getUserTenants(userId?: string): Promise<ApiResponse<TenantResponse[]>> {
|
||||
// If no userId provided, we'll get it from the auth store/token
|
||||
return apiClient.get(`${this.baseUrl}/users/${userId || 'current'}`);
|
||||
}
|
||||
|
||||
async validateTenantSlug(slug: string): Promise<ApiResponse<{ available: boolean; suggestions?: string[] }>> {
|
||||
return apiClient.get(`${this.baseUrl}/validate-slug/${slug}`);
|
||||
}
|
||||
|
||||
// Local state management helpers
|
||||
// Local state management helpers - Now uses tenant store
|
||||
getCurrentTenantId(): string | null {
|
||||
return localStorage.getItem('tenant_id');
|
||||
// This will be handled by the tenant store
|
||||
return null;
|
||||
}
|
||||
|
||||
getCurrentTenantData(): TenantResponse | null {
|
||||
const tenantData = localStorage.getItem('tenant_data');
|
||||
return tenantData ? JSON.parse(tenantData) : null;
|
||||
// This will be handled by the tenant store
|
||||
return null;
|
||||
}
|
||||
|
||||
setCurrentTenant(tenant: TenantResponse) {
|
||||
localStorage.setItem('tenant_id', tenant.id);
|
||||
localStorage.setItem('tenant_data', JSON.stringify(tenant));
|
||||
// This will be handled by the tenant store
|
||||
apiClient.setTenantId(tenant.id);
|
||||
}
|
||||
|
||||
clearCurrentTenant() {
|
||||
localStorage.removeItem('tenant_id');
|
||||
localStorage.removeItem('tenant_data');
|
||||
// This will be handled by the tenant store
|
||||
}
|
||||
|
||||
// Business type helpers
|
||||
|
||||
Reference in New Issue
Block a user