Date conversion is working from localstorage and via API
This commit is contained in:
parent
adb407b19a
commit
4330bd4b7c
@ -1,10 +1,19 @@
|
|||||||
// Persistent Authentication Hook with localStorage Integration
|
// Persistent Authentication Hook with localStorage Integration and Date Conversion
|
||||||
// Automatically restoring login state on page refresh
|
// Automatically restoring login state on page refresh with proper date handling
|
||||||
|
|
||||||
import React, { createContext, useContext,useState, useCallback, useEffect, useRef } from 'react';
|
import React, { createContext, useContext,useState, useCallback, useEffect, useRef } from 'react';
|
||||||
import * as Types from 'types/types';
|
import * as Types from 'types/types';
|
||||||
import { useUser } from 'hooks/useUser';
|
import { useUser } from 'hooks/useUser';
|
||||||
import { CreateCandidateRequest, CreateEmployerRequest, CreateViewerRequest, LoginRequest } from 'services/api-client';
|
import { CreateCandidateRequest, CreateEmployerRequest, CreateViewerRequest, LoginRequest } from 'services/api-client';
|
||||||
|
import { formatApiRequest } from 'types/conversion';
|
||||||
|
// Import date conversion functions
|
||||||
|
import {
|
||||||
|
convertCandidateFromApi,
|
||||||
|
convertEmployerFromApi,
|
||||||
|
convertViewerFromApi,
|
||||||
|
convertFromApi,
|
||||||
|
} from 'types/types';
|
||||||
|
|
||||||
|
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
user: Types.User | null;
|
user: Types.User | null;
|
||||||
@ -61,10 +70,59 @@ function clearStoredAuth(): void {
|
|||||||
localStorage.removeItem(TOKEN_STORAGE.TOKEN_EXPIRY);
|
localStorage.removeItem(TOKEN_STORAGE.TOKEN_EXPIRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert user data to storage format (dates to ISO strings)
|
||||||
|
*/
|
||||||
|
function prepareUserDataForStorage(user: Types.User): string {
|
||||||
|
try {
|
||||||
|
// Convert dates to ISO strings for storage
|
||||||
|
const userForStorage = formatApiRequest(user);
|
||||||
|
return JSON.stringify(userForStorage);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to prepare user data for storage:', error);
|
||||||
|
return JSON.stringify(user); // Fallback to direct serialization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert stored user data back to proper format (ISO strings to dates)
|
||||||
|
*/
|
||||||
|
function parseStoredUserData(userDataStr: string): Types.User | null {
|
||||||
|
try {
|
||||||
|
const rawUserData = JSON.parse(userDataStr);
|
||||||
|
|
||||||
|
// Determine user type and apply appropriate conversion
|
||||||
|
const userType = rawUserData.userType ||
|
||||||
|
(rawUserData.companyName ? 'employer' :
|
||||||
|
rawUserData.firstName ? 'candidate' : 'viewer');
|
||||||
|
|
||||||
|
switch (userType) {
|
||||||
|
case 'candidate':
|
||||||
|
return convertCandidateFromApi(rawUserData) as Types.Candidate;
|
||||||
|
case 'employer':
|
||||||
|
return convertEmployerFromApi(rawUserData) as Types.Employer;
|
||||||
|
case 'viewer':
|
||||||
|
return convertViewerFromApi(rawUserData) as Types.Viewer;
|
||||||
|
default:
|
||||||
|
// Fallback: try to determine by fields present
|
||||||
|
if (rawUserData.companyName) {
|
||||||
|
return convertEmployerFromApi(rawUserData) as Types.Employer;
|
||||||
|
} else if (rawUserData.skills || rawUserData.experience) {
|
||||||
|
return convertCandidateFromApi(rawUserData) as Types.Candidate;
|
||||||
|
} else {
|
||||||
|
return convertViewerFromApi(rawUserData) as Types.Viewer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to parse stored user data:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function storeAuthData(authResponse: Types.AuthResponse): void {
|
function storeAuthData(authResponse: Types.AuthResponse): void {
|
||||||
localStorage.setItem(TOKEN_STORAGE.ACCESS_TOKEN, authResponse.accessToken);
|
localStorage.setItem(TOKEN_STORAGE.ACCESS_TOKEN, authResponse.accessToken);
|
||||||
localStorage.setItem(TOKEN_STORAGE.REFRESH_TOKEN, authResponse.refreshToken);
|
localStorage.setItem(TOKEN_STORAGE.REFRESH_TOKEN, authResponse.refreshToken);
|
||||||
localStorage.setItem(TOKEN_STORAGE.USER_DATA, JSON.stringify(authResponse.user));
|
localStorage.setItem(TOKEN_STORAGE.USER_DATA, prepareUserDataForStorage(authResponse.user));
|
||||||
localStorage.setItem(TOKEN_STORAGE.TOKEN_EXPIRY, authResponse.expiresAt.toString());
|
localStorage.setItem(TOKEN_STORAGE.TOKEN_EXPIRY, authResponse.expiresAt.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,18 +142,31 @@ function getStoredAuthData(): {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (userDataStr) {
|
if (userDataStr) {
|
||||||
userData = JSON.parse(userDataStr);
|
userData = parseStoredUserData(userDataStr);
|
||||||
}
|
}
|
||||||
if (expiryStr) {
|
if (expiryStr) {
|
||||||
expiresAt = parseInt(expiryStr, 10);
|
expiresAt = parseInt(expiryStr, 10);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to parse stored auth data:', error);
|
console.error('Failed to parse stored auth data:', error);
|
||||||
|
// Clear corrupted data
|
||||||
|
clearStoredAuth();
|
||||||
}
|
}
|
||||||
|
|
||||||
return { accessToken, refreshToken, userData, expiresAt };
|
return { accessToken, refreshToken, userData, expiresAt };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update stored user data (useful when user profile is updated)
|
||||||
|
*/
|
||||||
|
function updateStoredUserData(user: Types.User): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(TOKEN_STORAGE.USER_DATA, prepareUserDataForStorage(user));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update stored user data:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function useSecureAuth() {
|
export function useSecureAuth() {
|
||||||
const [authState, setAuthState] = useState<AuthState>({
|
const [authState, setAuthState] = useState<AuthState>({
|
||||||
user: null,
|
user: null,
|
||||||
@ -108,10 +179,20 @@ export function useSecureAuth() {
|
|||||||
const {apiClient} = useUser();
|
const {apiClient} = useUser();
|
||||||
const initializationCompleted = useRef(false);
|
const initializationCompleted = useRef(false);
|
||||||
|
|
||||||
// Token refresh function
|
// Token refresh function with date conversion
|
||||||
const refreshAccessToken = useCallback(async (refreshToken: string): Promise<Types.AuthResponse | null> => {
|
const refreshAccessToken = useCallback(async (refreshToken: string): Promise<Types.AuthResponse | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.refreshToken(refreshToken);
|
const response = await apiClient.refreshToken(refreshToken);
|
||||||
|
|
||||||
|
// Ensure user data has proper date conversion
|
||||||
|
if (response.user) {
|
||||||
|
const userType = response.user.userType ||
|
||||||
|
(response.user.companyName ? 'employer' :
|
||||||
|
response.user.firstName ? 'candidate' : 'viewer');
|
||||||
|
|
||||||
|
response.user = convertFromApi<Types.User>(response.user, userType);
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Token refresh failed:', error);
|
console.error('Token refresh failed:', error);
|
||||||
@ -147,11 +228,11 @@ export function useSecureAuth() {
|
|||||||
const refreshResult = await refreshAccessToken(stored.refreshToken);
|
const refreshResult = await refreshAccessToken(stored.refreshToken);
|
||||||
|
|
||||||
if (refreshResult) {
|
if (refreshResult) {
|
||||||
// Successfully refreshed
|
// Successfully refreshed - store with proper date conversion
|
||||||
storeAuthData(refreshResult);
|
storeAuthData(refreshResult);
|
||||||
apiClient.setAuthToken(refreshResult.accessToken);
|
apiClient.setAuthToken(refreshResult.accessToken);
|
||||||
|
|
||||||
console.log("User =>", refreshResult.user);
|
console.log("User (refreshed) =>", refreshResult.user);
|
||||||
|
|
||||||
setAuthState({
|
setAuthState({
|
||||||
user: refreshResult.user,
|
user: refreshResult.user,
|
||||||
@ -161,7 +242,7 @@ export function useSecureAuth() {
|
|||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Token refreshed successfully');
|
console.log('Token refreshed successfully with date conversion');
|
||||||
} else {
|
} else {
|
||||||
// Refresh failed, clear stored data
|
// Refresh failed, clear stored data
|
||||||
console.log('Token refresh failed, clearing stored auth');
|
console.log('Token refresh failed, clearing stored auth');
|
||||||
@ -177,19 +258,19 @@ export function useSecureAuth() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Access token is still valid
|
// Access token is still valid - user data already has date conversion applied
|
||||||
apiClient.setAuthToken(stored.accessToken);
|
apiClient.setAuthToken(stored.accessToken);
|
||||||
|
|
||||||
console.log("User =>", stored.userData);
|
console.log("User (from storage) =>", stored.userData);
|
||||||
setAuthState({
|
setAuthState({
|
||||||
user: stored.userData,
|
user: stored.userData, // Already converted by parseStoredUserData
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isInitializing: false,
|
isInitializing: false,
|
||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Restored authentication from stored tokens');
|
console.log('Restored authentication from stored tokens with date conversion');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error initializing auth:', error);
|
console.error('Error initializing auth:', error);
|
||||||
@ -250,7 +331,16 @@ export function useSecureAuth() {
|
|||||||
try {
|
try {
|
||||||
const authResponse = await apiClient.login(loginData);
|
const authResponse = await apiClient.login(loginData);
|
||||||
|
|
||||||
// Store tokens and user data
|
// Ensure user data has proper date conversion before storing
|
||||||
|
if (authResponse.user) {
|
||||||
|
const userType = authResponse.user.userType ||
|
||||||
|
(authResponse.user.companyName ? 'employer' :
|
||||||
|
authResponse.user.firstName ? 'candidate' : 'viewer');
|
||||||
|
|
||||||
|
authResponse.user = convertFromApi<Types.User>(authResponse.user, userType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store tokens and user data with date conversion
|
||||||
storeAuthData(authResponse);
|
storeAuthData(authResponse);
|
||||||
|
|
||||||
// Update API client with new token
|
// Update API client with new token
|
||||||
@ -264,7 +354,7 @@ export function useSecureAuth() {
|
|||||||
error: null
|
error: null
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Login successful');
|
console.log('Login successful with date conversion applied');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Login failed';
|
const errorMessage = error instanceof Error ? error.message : 'Login failed';
|
||||||
@ -295,6 +385,20 @@ export function useSecureAuth() {
|
|||||||
console.log('User logged out');
|
console.log('User logged out');
|
||||||
}, [apiClient]);
|
}, [apiClient]);
|
||||||
|
|
||||||
|
// Update user data in both state and localStorage (with date conversion)
|
||||||
|
const updateUserData = useCallback((updatedUser: Types.User) => {
|
||||||
|
// Update localStorage with proper date formatting
|
||||||
|
updateStoredUserData(updatedUser);
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
setAuthState(prev => ({
|
||||||
|
...prev,
|
||||||
|
user: updatedUser
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log('User data updated with date conversion');
|
||||||
|
}, []);
|
||||||
|
|
||||||
const createViewerAccount = useCallback(async (viewerData: CreateViewerRequest): Promise<boolean> => {
|
const createViewerAccount = useCallback(async (viewerData: CreateViewerRequest): Promise<boolean> => {
|
||||||
setAuthState(prev => ({ ...prev, isLoading: true, error: null }));
|
setAuthState(prev => ({ ...prev, isLoading: true, error: null }));
|
||||||
|
|
||||||
@ -316,7 +420,9 @@ export function useSecureAuth() {
|
|||||||
throw new Error(usernameValidation.issues.join(', '));
|
throw new Error(usernameValidation.issues.join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create viewer - API client automatically applies date conversion
|
||||||
const viewer = await apiClient.createViewer(viewerData);
|
const viewer = await apiClient.createViewer(viewerData);
|
||||||
|
console.log('Viewer created with date conversion:', viewer);
|
||||||
|
|
||||||
// Auto-login after successful registration
|
// Auto-login after successful registration
|
||||||
const loginSuccess = await login({
|
const loginSuccess = await login({
|
||||||
@ -357,7 +463,9 @@ export function useSecureAuth() {
|
|||||||
throw new Error(usernameValidation.issues.join(', '));
|
throw new Error(usernameValidation.issues.join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create candidate - API client automatically applies date conversion
|
||||||
const candidate = await apiClient.createCandidate(candidateData);
|
const candidate = await apiClient.createCandidate(candidateData);
|
||||||
|
console.log('Candidate created with date conversion:', candidate);
|
||||||
|
|
||||||
// Auto-login after successful registration
|
// Auto-login after successful registration
|
||||||
const loginSuccess = await login({
|
const loginSuccess = await login({
|
||||||
@ -398,7 +506,9 @@ export function useSecureAuth() {
|
|||||||
throw new Error(usernameValidation.issues.join(', '));
|
throw new Error(usernameValidation.issues.join(', '));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create employer - API client automatically applies date conversion
|
||||||
const employer = await apiClient.createEmployer(employerData);
|
const employer = await apiClient.createEmployer(employerData);
|
||||||
|
console.log('Employer created with date conversion:', employer);
|
||||||
|
|
||||||
// Auto-login after successful registration
|
// Auto-login after successful registration
|
||||||
const loginSuccess = await login({
|
const loginSuccess = await login({
|
||||||
@ -474,7 +584,8 @@ export function useSecureAuth() {
|
|||||||
createCandidateAccount,
|
createCandidateAccount,
|
||||||
createEmployerAccount,
|
createEmployerAccount,
|
||||||
requestPasswordReset,
|
requestPasswordReset,
|
||||||
refreshAuth
|
refreshAuth,
|
||||||
|
updateUserData // New function to update user data with proper storage
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,7 +620,7 @@ export function useAuth() {
|
|||||||
interface ProtectedRouteProps {
|
interface ProtectedRouteProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
fallback?: React.ReactNode;
|
fallback?: React.ReactNode;
|
||||||
requiredUserType?: 'candidate' | 'employer';
|
requiredUserType?: 'candidate' | 'employer' | 'viewer';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProtectedRoute({
|
export function ProtectedRoute({
|
||||||
@ -538,7 +649,7 @@ export function ProtectedRoute({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Usage Examples
|
// Usage Examples with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -580,7 +691,7 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component using auth
|
// Component using auth with proper date handling
|
||||||
function Header() {
|
function Header() {
|
||||||
const { user, isAuthenticated, logout, isInitializing } = useAuth();
|
const { user, isAuthenticated, logout, isInitializing } = useAuth();
|
||||||
|
|
||||||
@ -591,8 +702,16 @@ function Header() {
|
|||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
{isAuthenticated ? (
|
{isAuthenticated ? (
|
||||||
|
<div>
|
||||||
<div>
|
<div>
|
||||||
Welcome, {user?.firstName || user?.companyName}!
|
Welcome, {user?.firstName || user?.companyName}!
|
||||||
|
{user?.createdAt && (
|
||||||
|
<small>Member since {user.createdAt.toLocaleDateString()}</small>
|
||||||
|
)}
|
||||||
|
{user?.lastLogin && (
|
||||||
|
<small>Last login: {user.lastLogin.toLocaleString()}</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<button onClick={logout}>Logout</button>
|
<button onClick={logout}>Logout</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -605,6 +724,48 @@ function Header() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Profile component with date operations
|
||||||
|
function UserProfile() {
|
||||||
|
const { user, updateUserData } = useAuth();
|
||||||
|
|
||||||
|
if (!user) return null;
|
||||||
|
|
||||||
|
// All date operations work properly because dates are Date objects
|
||||||
|
const accountAge = user.createdAt ?
|
||||||
|
Math.floor((Date.now() - user.createdAt.getTime()) / (1000 * 60 * 60 * 24)) : 0;
|
||||||
|
|
||||||
|
const handleProfileUpdate = async (updates: Partial<Types.User>) => {
|
||||||
|
// When updating user data, it will be properly stored with date conversion
|
||||||
|
const updatedUser = { ...user, ...updates, updatedAt: new Date() };
|
||||||
|
updateUserData(updatedUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Profile</h2>
|
||||||
|
<p>Account created: {user.createdAt?.toLocaleDateString()}</p>
|
||||||
|
<p>Account age: {accountAge} days</p>
|
||||||
|
{user.lastLogin && (
|
||||||
|
<p>Last login: {user.lastLogin.toLocaleString()}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{'availabilityDate' in user && user.availabilityDate && (
|
||||||
|
<p>Available from: {user.availabilityDate.toLocaleDateString()}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{'experience' in user && user.experience?.map((exp, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<h4>{exp.position} at {exp.companyName}</h4>
|
||||||
|
<p>
|
||||||
|
{exp.startDate.toLocaleDateString()} -
|
||||||
|
{exp.endDate ? exp.endDate.toLocaleDateString() : 'Present'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-redirect based on auth state
|
// Auto-redirect based on auth state
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
const { isAuthenticated, isInitializing } = useAuth();
|
const { isAuthenticated, isInitializing } = useAuth();
|
||||||
|
@ -290,7 +290,6 @@ const LoginPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(user);
|
|
||||||
// If user is logged in, show their profile
|
// If user is logged in, show their profile
|
||||||
if (user) {
|
if (user) {
|
||||||
return (
|
return (
|
||||||
@ -321,7 +320,7 @@ const LoginPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 12, md: 6 }}>
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<Typography variant="body1" sx={{ mb: 1 }}>
|
<Typography variant="body1" sx={{ mb: 1 }}>
|
||||||
<strong>Status:</strong> {user.status}
|
{/* <strong>Status:</strong> {user.status} */}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid size={{ xs: 12, md: 6 }}>
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Enhanced API Client with Streaming Support
|
* Enhanced API Client with Streaming Support and Date Conversion
|
||||||
*
|
*
|
||||||
* This demonstrates how to use the generated types with the conversion utilities
|
* This demonstrates how to use the generated types with the conversion utilities
|
||||||
* for seamless frontend-backend communication, including streaming responses.
|
* for seamless frontend-backend communication, including streaming responses and
|
||||||
|
* automatic date field conversion.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Import generated types (from running generate_types.py)
|
// Import generated types (from running generate_types.py)
|
||||||
@ -21,6 +22,19 @@ import {
|
|||||||
PaginatedRequest
|
PaginatedRequest
|
||||||
} from 'types/conversion';
|
} from 'types/conversion';
|
||||||
|
|
||||||
|
// Import generated date conversion functions
|
||||||
|
import {
|
||||||
|
convertCandidateFromApi,
|
||||||
|
convertEmployerFromApi,
|
||||||
|
convertJobFromApi,
|
||||||
|
convertJobApplicationFromApi,
|
||||||
|
convertChatSessionFromApi,
|
||||||
|
convertChatMessageFromApi,
|
||||||
|
convertViewerFromApi,
|
||||||
|
convertFromApi,
|
||||||
|
convertArrayFromApi
|
||||||
|
} from 'types/types';
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Streaming Types and Interfaces
|
// Streaming Types and Interfaces
|
||||||
// ============================
|
// ============================
|
||||||
@ -135,6 +149,61 @@ class ApiClient {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================
|
||||||
|
// Enhanced Response Handlers with Date Conversion
|
||||||
|
// ============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle API response with automatic date conversion for specific model types
|
||||||
|
*/
|
||||||
|
private async handleApiResponseWithConversion<T>(
|
||||||
|
response: Response,
|
||||||
|
modelType?: string
|
||||||
|
): Promise<T> {
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const apiResponse = parseApiResponse<T>(data);
|
||||||
|
const extractedData = extractApiData(apiResponse);
|
||||||
|
|
||||||
|
// Apply model-specific date conversion if modelType is provided
|
||||||
|
if (modelType) {
|
||||||
|
return convertFromApi<T>(extractedData, modelType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle paginated API response with automatic date conversion
|
||||||
|
*/
|
||||||
|
private async handlePaginatedApiResponseWithConversion<T>(
|
||||||
|
response: Response,
|
||||||
|
modelType?: string
|
||||||
|
): Promise<PaginatedResponse<T>> {
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const apiResponse = parsePaginatedResponse<T>(data);
|
||||||
|
const extractedData = extractApiData(apiResponse);
|
||||||
|
|
||||||
|
// Apply model-specific date conversion to array items if modelType is provided
|
||||||
|
if (modelType && extractedData.data) {
|
||||||
|
return {
|
||||||
|
...extractedData,
|
||||||
|
data: convertArrayFromApi<T>(extractedData.data, modelType)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractedData;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Authentication Methods
|
// Authentication Methods
|
||||||
// ============================
|
// ============================
|
||||||
@ -145,6 +214,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(request))
|
body: JSON.stringify(formatApiRequest(request))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// AuthResponse doesn't typically have date fields, use standard handler
|
||||||
return handleApiResponse<Types.AuthResponse>(response);
|
return handleApiResponse<Types.AuthResponse>(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +239,7 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Viewer Methods
|
// Viewer Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async createViewer(request: CreateViewerRequest): Promise<Types.Viewer> {
|
async createViewer(request: CreateViewerRequest): Promise<Types.Viewer> {
|
||||||
@ -179,11 +249,11 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(request))
|
body: JSON.stringify(formatApiRequest(request))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Viewer>(response);
|
return this.handleApiResponseWithConversion<Types.Viewer>(response, 'Viewer');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Candidate Methods
|
// Candidate Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async createCandidate(request: CreateCandidateRequest): Promise<Types.Candidate> {
|
async createCandidate(request: CreateCandidateRequest): Promise<Types.Candidate> {
|
||||||
@ -193,7 +263,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(request))
|
body: JSON.stringify(formatApiRequest(request))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Candidate>(response);
|
return this.handleApiResponseWithConversion<Types.Candidate>(response, 'Candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCandidate(username: string): Promise<Types.Candidate> {
|
async getCandidate(username: string): Promise<Types.Candidate> {
|
||||||
@ -201,7 +271,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Candidate>(response);
|
return this.handleApiResponseWithConversion<Types.Candidate>(response, 'Candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCandidate(id: string, updates: Partial<Types.Candidate>): Promise<Types.Candidate> {
|
async updateCandidate(id: string, updates: Partial<Types.Candidate>): Promise<Types.Candidate> {
|
||||||
@ -211,7 +281,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(updates))
|
body: JSON.stringify(formatApiRequest(updates))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Candidate>(response);
|
return this.handleApiResponseWithConversion<Types.Candidate>(response, 'Candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCandidates(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Candidate>> {
|
async getCandidates(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Candidate>> {
|
||||||
@ -222,7 +292,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.Candidate>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.Candidate>(response, 'Candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchCandidates(query: string, filters?: Record<string, any>): Promise<PaginatedResponse<Types.Candidate>> {
|
async searchCandidates(query: string, filters?: Record<string, any>): Promise<PaginatedResponse<Types.Candidate>> {
|
||||||
@ -238,11 +308,11 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.Candidate>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.Candidate>(response, 'Candidate');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Employer Methods
|
// Employer Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async createEmployer(request: CreateEmployerRequest): Promise<Types.Employer> {
|
async createEmployer(request: CreateEmployerRequest): Promise<Types.Employer> {
|
||||||
@ -252,7 +322,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(request))
|
body: JSON.stringify(formatApiRequest(request))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Employer>(response);
|
return this.handleApiResponseWithConversion<Types.Employer>(response, 'Employer');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEmployer(id: string): Promise<Types.Employer> {
|
async getEmployer(id: string): Promise<Types.Employer> {
|
||||||
@ -260,7 +330,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Employer>(response);
|
return this.handleApiResponseWithConversion<Types.Employer>(response, 'Employer');
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEmployer(id: string, updates: Partial<Types.Employer>): Promise<Types.Employer> {
|
async updateEmployer(id: string, updates: Partial<Types.Employer>): Promise<Types.Employer> {
|
||||||
@ -270,11 +340,11 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(updates))
|
body: JSON.stringify(formatApiRequest(updates))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Employer>(response);
|
return this.handleApiResponseWithConversion<Types.Employer>(response, 'Employer');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Job Methods
|
// Job Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async createJob(job: Omit<Types.Job, 'id' | 'datePosted' | 'views' | 'applicationCount'>): Promise<Types.Job> {
|
async createJob(job: Omit<Types.Job, 'id' | 'datePosted' | 'views' | 'applicationCount'>): Promise<Types.Job> {
|
||||||
@ -284,7 +354,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(job))
|
body: JSON.stringify(formatApiRequest(job))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Job>(response);
|
return this.handleApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJob(id: string): Promise<Types.Job> {
|
async getJob(id: string): Promise<Types.Job> {
|
||||||
@ -292,7 +362,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.Job>(response);
|
return this.handleApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJobs(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Job>> {
|
async getJobs(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Job>> {
|
||||||
@ -303,7 +373,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.Job>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJobsByEmployer(employerId: string, request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Job>> {
|
async getJobsByEmployer(employerId: string, request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.Job>> {
|
||||||
@ -314,7 +384,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.Job>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchJobs(query: string, filters?: Record<string, any>): Promise<PaginatedResponse<Types.Job>> {
|
async searchJobs(query: string, filters?: Record<string, any>): Promise<PaginatedResponse<Types.Job>> {
|
||||||
@ -330,11 +400,11 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.Job>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Job Application Methods
|
// Job Application Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async applyToJob(application: Omit<Types.JobApplication, 'id' | 'appliedDate' | 'updatedDate' | 'status'>): Promise<Types.JobApplication> {
|
async applyToJob(application: Omit<Types.JobApplication, 'id' | 'appliedDate' | 'updatedDate' | 'status'>): Promise<Types.JobApplication> {
|
||||||
@ -344,7 +414,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(application))
|
body: JSON.stringify(formatApiRequest(application))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.JobApplication>(response);
|
return this.handleApiResponseWithConversion<Types.JobApplication>(response, 'JobApplication');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJobApplication(id: string): Promise<Types.JobApplication> {
|
async getJobApplication(id: string): Promise<Types.JobApplication> {
|
||||||
@ -352,7 +422,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.JobApplication>(response);
|
return this.handleApiResponseWithConversion<Types.JobApplication>(response, 'JobApplication');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getJobApplications(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.JobApplication>> {
|
async getJobApplications(request: Partial<PaginatedRequest> = {}): Promise<PaginatedResponse<Types.JobApplication>> {
|
||||||
@ -363,7 +433,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.JobApplication>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.JobApplication>(response, 'JobApplication');
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateApplicationStatus(id: string, status: Types.ApplicationStatus): Promise<Types.JobApplication> {
|
async updateApplicationStatus(id: string, status: Types.ApplicationStatus): Promise<Types.JobApplication> {
|
||||||
@ -373,11 +443,11 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest({ status }))
|
body: JSON.stringify(formatApiRequest({ status }))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.JobApplication>(response);
|
return this.handleApiResponseWithConversion<Types.JobApplication>(response, 'JobApplication');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Chat Methods
|
// Chat Methods with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,7 +462,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest(request))
|
body: JSON.stringify(formatApiRequest(request))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.ChatSession>(response);
|
return this.handleApiResponseWithConversion<Types.ChatSession>(response, 'ChatSession');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -409,7 +479,15 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<CandidateSessionsResponse>(response);
|
// Handle the nested sessions with date conversion
|
||||||
|
const result = await this.handleApiResponseWithConversion<CandidateSessionsResponse>(response);
|
||||||
|
|
||||||
|
// Convert the nested sessions array
|
||||||
|
if (result.sessions && result.sessions.data) {
|
||||||
|
result.sessions.data = convertArrayFromApi<Types.ChatSession>(result.sessions.data, 'ChatSession');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -439,7 +517,7 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest({ context }))
|
body: JSON.stringify(formatApiRequest({ context }))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.ChatSession>(response);
|
return this.handleApiResponseWithConversion<Types.ChatSession>(response, 'ChatSession');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChatSession(id: string): Promise<Types.ChatSession> {
|
async getChatSession(id: string): Promise<Types.ChatSession> {
|
||||||
@ -447,7 +525,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.ChatSession>(response);
|
return this.handleApiResponseWithConversion<Types.ChatSession>(response, 'ChatSession');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -460,11 +538,11 @@ class ApiClient {
|
|||||||
body: JSON.stringify(formatApiRequest({query}))
|
body: JSON.stringify(formatApiRequest({query}))
|
||||||
});
|
});
|
||||||
|
|
||||||
return handleApiResponse<Types.ChatMessage>(response);
|
return this.handleApiResponseWithConversion<Types.ChatMessage>(response, 'ChatMessage');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send message with streaming response support
|
* Send message with streaming response support and date conversion
|
||||||
*/
|
*/
|
||||||
sendMessageStream(
|
sendMessageStream(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
@ -526,30 +604,35 @@ class ApiClient {
|
|||||||
const data = line.slice(5).trim();
|
const data = line.slice(5).trim();
|
||||||
const incoming: Types.ChatMessageBase = JSON.parse(data);
|
const incoming: Types.ChatMessageBase = JSON.parse(data);
|
||||||
|
|
||||||
|
// Convert date fields for incoming messages
|
||||||
|
const convertedIncoming = convertChatMessageFromApi(incoming);
|
||||||
|
|
||||||
// Trigger callbacks based on status
|
// Trigger callbacks based on status
|
||||||
if (incoming.status !== chatMessage?.status) {
|
if (convertedIncoming.status !== chatMessage?.status) {
|
||||||
options.onStatusChange?.(incoming.status);
|
options.onStatusChange?.(convertedIncoming.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle different status types
|
// Handle different status types
|
||||||
switch (incoming.status) {
|
switch (convertedIncoming.status) {
|
||||||
case 'streaming':
|
case 'streaming':
|
||||||
if (chatMessage === null) {
|
if (chatMessage === null) {
|
||||||
chatMessage = {...incoming};
|
chatMessage = {...convertedIncoming};
|
||||||
} else {
|
} else {
|
||||||
// Can't do a simple += as typescript thinks .content might not be there
|
// Can't do a simple += as typescript thinks .content might not be there
|
||||||
chatMessage.content = (chatMessage?.content || '') + incoming.content;
|
chatMessage.content = (chatMessage?.content || '') + convertedIncoming.content;
|
||||||
|
// Update timestamp to latest
|
||||||
|
chatMessage.timestamp = convertedIncoming.timestamp;
|
||||||
}
|
}
|
||||||
options.onStreaming?.(incoming);
|
options.onStreaming?.(convertedIncoming);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
options.onError?.(incoming);
|
options.onError?.(convertedIncoming);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
chatMessageList.push(incoming);
|
chatMessageList.push(convertedIncoming);
|
||||||
options.onMessage?.(incoming);
|
options.onMessage?.(convertedIncoming);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -606,7 +689,7 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get persisted chat messages for a session
|
* Get persisted chat messages for a session with date conversion
|
||||||
*/
|
*/
|
||||||
async getChatMessages(
|
async getChatMessages(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
@ -621,7 +704,7 @@ class ApiClient {
|
|||||||
headers: this.defaultHeaders
|
headers: this.defaultHeaders
|
||||||
});
|
});
|
||||||
|
|
||||||
return handlePaginatedApiResponse<Types.ChatMessage>(response);
|
return this.handlePaginatedApiResponseWithConversion<Types.ChatMessage>(response, 'ChatMessage');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
@ -712,10 +795,10 @@ class ApiClient {
|
|||||||
// Error Handling Helper
|
// Error Handling Helper
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
async handleRequest<T>(requestFn: () => Promise<Response>): Promise<T> {
|
async handleRequest<T>(requestFn: () => Promise<Response>, modelType?: string): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const response = await requestFn();
|
const response = await requestFn();
|
||||||
return await handleApiResponse<T>(response);
|
return await this.handleApiResponseWithConversion<T>(response, modelType);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('API request failed:', error);
|
console.error('API request failed:', error);
|
||||||
throw error;
|
throw error;
|
||||||
@ -740,10 +823,10 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// React Hooks for Streaming
|
// React Hooks for Streaming with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
/* React Hook Examples for Streaming Chat
|
/* React Hook Examples for Streaming Chat with proper date handling
|
||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
|
||||||
export function useStreamingChat(sessionId: string) {
|
export function useStreamingChat(sessionId: string) {
|
||||||
@ -762,31 +845,39 @@ export function useStreamingChat(sessionId: string) {
|
|||||||
|
|
||||||
const streamingOptions: StreamingOptions = {
|
const streamingOptions: StreamingOptions = {
|
||||||
onMessage: (message) => {
|
onMessage: (message) => {
|
||||||
|
// Message already has proper Date objects from conversion
|
||||||
setCurrentMessage(message);
|
setCurrentMessage(message);
|
||||||
},
|
},
|
||||||
onPartialMessage: (content, messageId) => {
|
onStreaming: (chunk) => {
|
||||||
|
// Chunk also has proper Date objects
|
||||||
setCurrentMessage(prev => prev ?
|
setCurrentMessage(prev => prev ?
|
||||||
{ ...prev, content: prev.content + content } :
|
|
||||||
{
|
{
|
||||||
id: messageId || '',
|
...prev,
|
||||||
|
content: prev.content + chunk.content,
|
||||||
|
timestamp: chunk.timestamp // Update to latest timestamp
|
||||||
|
} :
|
||||||
|
{
|
||||||
|
id: chunk.id || '',
|
||||||
sessionId,
|
sessionId,
|
||||||
status: 'streaming',
|
status: 'streaming',
|
||||||
sender: 'ai',
|
sender: 'ai',
|
||||||
content,
|
content: chunk.content,
|
||||||
timestamp: new Date()
|
timestamp: chunk.timestamp // Already a Date object
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onStatusChange: (status) => {
|
onStatusChange: (status) => {
|
||||||
setCurrentMessage(prev => prev ? { ...prev, status } : null);
|
setCurrentMessage(prev => prev ? { ...prev, status } : null);
|
||||||
},
|
},
|
||||||
onComplete: (finalMessage) => {
|
onComplete: () => {
|
||||||
setMessages(prev => [...prev, finalMessage]);
|
if (currentMessage) {
|
||||||
|
setMessages(prev => [...prev, currentMessage]);
|
||||||
|
}
|
||||||
setCurrentMessage(null);
|
setCurrentMessage(null);
|
||||||
setIsStreaming(false);
|
setIsStreaming(false);
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
setError(err.message);
|
setError(typeof err === 'string' ? err : err.content);
|
||||||
setIsStreaming(false);
|
setIsStreaming(false);
|
||||||
setCurrentMessage(null);
|
setCurrentMessage(null);
|
||||||
}
|
}
|
||||||
@ -799,7 +890,7 @@ export function useStreamingChat(sessionId: string) {
|
|||||||
setError(err instanceof Error ? err.message : 'Failed to send message');
|
setError(err instanceof Error ? err.message : 'Failed to send message');
|
||||||
setIsStreaming(false);
|
setIsStreaming(false);
|
||||||
}
|
}
|
||||||
}, [sessionId, apiClient]);
|
}, [sessionId, apiClient, currentMessage]);
|
||||||
|
|
||||||
const cancelStreaming = useCallback(() => {
|
const cancelStreaming = useCallback(() => {
|
||||||
if (streamingRef.current) {
|
if (streamingRef.current) {
|
||||||
@ -819,7 +910,7 @@ export function useStreamingChat(sessionId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage in React component:
|
// Usage in React component with proper date handling:
|
||||||
function ChatInterface({ sessionId }: { sessionId: string }) {
|
function ChatInterface({ sessionId }: { sessionId: string }) {
|
||||||
const {
|
const {
|
||||||
messages,
|
messages,
|
||||||
@ -839,15 +930,27 @@ function ChatInterface({ sessionId }: { sessionId: string }) {
|
|||||||
<div className="messages">
|
<div className="messages">
|
||||||
{messages.map(message => (
|
{messages.map(message => (
|
||||||
<div key={message.id}>
|
<div key={message.id}>
|
||||||
<strong>{message.sender}:</strong> {message.content}
|
<div className="message-header">
|
||||||
|
<strong>{message.sender}:</strong>
|
||||||
|
<span className="timestamp">
|
||||||
|
{message.timestamp.toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="message-content">{message.content}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{currentMessage && (
|
{currentMessage && (
|
||||||
<div className="current-message">
|
<div className="current-message">
|
||||||
<strong>{currentMessage.sender}:</strong> {currentMessage.content}
|
<div className="message-header">
|
||||||
|
<strong>{currentMessage.sender}:</strong>
|
||||||
|
<span className="timestamp">
|
||||||
|
{currentMessage.timestamp.toLocaleTimeString()}
|
||||||
|
</span>
|
||||||
{isStreaming && <span className="streaming-indicator">...</span>}
|
{isStreaming && <span className="streaming-indicator">...</span>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="message-content">{currentMessage.content}</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -874,61 +977,96 @@ function ChatInterface({ sessionId }: { sessionId: string }) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
// Usage Examples
|
// Usage Examples with Date Conversion
|
||||||
// ============================
|
// ============================
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Initialize API client
|
// Initialize API client
|
||||||
const apiClient = new ApiClient();
|
const apiClient = new ApiClient();
|
||||||
|
|
||||||
// Standard message sending (non-streaming)
|
// All returned objects now have proper Date fields automatically!
|
||||||
|
|
||||||
|
// Create a candidate - createdAt, updatedAt, lastLogin are Date objects
|
||||||
try {
|
try {
|
||||||
const message = await apiClient.sendMessage(sessionId, 'Hello, how are you?');
|
const candidate = await apiClient.createCandidate({
|
||||||
console.log('Response:', message.content);
|
email: 'jane@example.com',
|
||||||
|
username: 'jane_doe',
|
||||||
|
password: 'SecurePassword123!',
|
||||||
|
firstName: 'Jane',
|
||||||
|
lastName: 'Doe'
|
||||||
|
});
|
||||||
|
|
||||||
|
// These are now Date objects, not strings!
|
||||||
|
console.log('Created at:', candidate.createdAt.toLocaleDateString());
|
||||||
|
console.log('Profile created on:', candidate.createdAt.toDateString());
|
||||||
|
|
||||||
|
if (candidate.lastLogin) {
|
||||||
|
console.log('Last seen:', candidate.lastLogin.toRelativeTimeString());
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to send message:', error);
|
console.error('Failed to create candidate:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streaming message with callbacks
|
// Get jobs with proper date conversion
|
||||||
const streamResponse = apiClient.sendMessageStream(sessionId, 'Tell me a long story', {
|
try {
|
||||||
onPartialMessage: (content, messageId) => {
|
const jobs = await apiClient.getJobs({ limit: 10 });
|
||||||
console.log('Partial content:', content);
|
|
||||||
// Update UI with partial content
|
jobs.data.forEach(job => {
|
||||||
|
// datePosted, applicationDeadline, featuredUntil are Date objects
|
||||||
|
console.log(`${job.title} - Posted: ${job.datePosted.toLocaleDateString()}`);
|
||||||
|
|
||||||
|
if (job.applicationDeadline) {
|
||||||
|
const daysRemaining = Math.ceil(
|
||||||
|
(job.applicationDeadline.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24)
|
||||||
|
);
|
||||||
|
console.log(`Deadline in ${daysRemaining} days`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch jobs:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming with proper date conversion
|
||||||
|
const streamResponse = apiClient.sendMessageStream(sessionId, 'Tell me about job opportunities', {
|
||||||
|
onStreaming: (chunk) => {
|
||||||
|
// chunk.timestamp is a Date object
|
||||||
|
console.log(`Streaming at ${chunk.timestamp.toLocaleTimeString()}:`, chunk.content);
|
||||||
},
|
},
|
||||||
onStatusChange: (status) => {
|
onMessage: (message) => {
|
||||||
console.log('Status changed:', status);
|
// message.timestamp is a Date object
|
||||||
// Update UI status indicator
|
console.log(`Final message at ${message.timestamp.toLocaleTimeString()}:`, message.content);
|
||||||
},
|
},
|
||||||
onComplete: (finalMessage) => {
|
onComplete: () => {
|
||||||
console.log('Final message:', finalMessage.content);
|
console.log('Streaming completed');
|
||||||
// Handle completed message
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.error('Streaming error:', error);
|
|
||||||
// Handle error
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Can cancel the stream if needed
|
// Chat sessions with date conversion
|
||||||
setTimeout(() => {
|
|
||||||
streamResponse.cancel();
|
|
||||||
}, 10000); // Cancel after 10 seconds
|
|
||||||
|
|
||||||
// Wait for completion
|
|
||||||
try {
|
try {
|
||||||
const finalMessage = await streamResponse.promise;
|
const chatSession = await apiClient.createChatSession({
|
||||||
console.log('Stream completed:', finalMessage);
|
type: 'job_search',
|
||||||
|
additionalContext: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// createdAt and lastActivity are Date objects
|
||||||
|
console.log('Session created:', chatSession.createdAt.toISOString());
|
||||||
|
console.log('Last activity:', chatSession.lastActivity.toLocaleDateString());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Stream failed:', error);
|
console.error('Failed to create chat session:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-detection: streaming if callbacks provided, standard otherwise
|
// Get chat messages with date conversion
|
||||||
await apiClient.sendMessageAuto(sessionId, 'Quick question', {
|
try {
|
||||||
onPartialMessage: (content) => console.log('Streaming:', content)
|
const messages = await apiClient.getChatMessages(sessionId);
|
||||||
}); // Will use streaming
|
|
||||||
|
|
||||||
await apiClient.sendMessageAuto(sessionId, 'Quick question'); // Will use standard
|
messages.data.forEach(message => {
|
||||||
|
// timestamp is a Date object
|
||||||
|
console.log(`[${message.timestamp.toLocaleString()}] ${message.sender}: ${message.content}`);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch messages:', error);
|
||||||
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { ApiClient }
|
export { ApiClient }
|
||||||
export type { StreamingOptions, StreamingResponse };
|
export type { StreamingOptions, StreamingResponse }
|
@ -1,6 +1,6 @@
|
|||||||
// Generated TypeScript types from Pydantic models
|
// Generated TypeScript types from Pydantic models
|
||||||
// Source: src/backend/models.py
|
// Source: src/backend/models.py
|
||||||
// Generated on: 2025-05-30T09:14:59.413256
|
// Generated on: 2025-05-30T09:39:47.716115
|
||||||
// DO NOT EDIT MANUALLY - This file is auto-generated
|
// DO NOT EDIT MANUALLY - This file is auto-generated
|
||||||
|
|
||||||
// ============================
|
// ============================
|
||||||
@ -712,370 +712,400 @@ export interface WorkExperience {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert Analytics from API response, parsing date fields
|
* Convert Analytics from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertAnalyticsFromApi(data: any): Analytics {
|
export function convertAnalyticsFromApi(data: any): Analytics {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
entityType: new Date(data.entityType),
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert ApplicationDecision from API response, parsing date fields
|
* Convert ApplicationDecision from API response, parsing date fields
|
||||||
|
* Date fields: date
|
||||||
*/
|
*/
|
||||||
export function convertApplicationDecisionFromApi(data: any): ApplicationDecision {
|
export function convertApplicationDecisionFromApi(data: any): ApplicationDecision {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert date from ISO string to Date
|
||||||
date: new Date(data.date),
|
date: new Date(data.date),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Attachment from API response, parsing date fields
|
* Convert Attachment from API response, parsing date fields
|
||||||
|
* Date fields: uploadedAt
|
||||||
*/
|
*/
|
||||||
export function convertAttachmentFromApi(data: any): Attachment {
|
export function convertAttachmentFromApi(data: any): Attachment {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert uploadedAt from ISO string to Date
|
||||||
uploadedAt: new Date(data.uploadedAt),
|
uploadedAt: new Date(data.uploadedAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Convert AuthResponse from API response, parsing date fields
|
|
||||||
*/
|
|
||||||
export function convertAuthResponseFromApi(data: any): AuthResponse {
|
|
||||||
if (!data) return data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
user: new Date(data.user),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Convert Authentication from API response, parsing date fields
|
* Convert Authentication from API response, parsing date fields
|
||||||
|
* Date fields: resetPasswordExpiry, lastPasswordChange, lockedUntil
|
||||||
*/
|
*/
|
||||||
export function convertAuthenticationFromApi(data: any): Authentication {
|
export function convertAuthenticationFromApi(data: any): Authentication {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert resetPasswordExpiry from ISO string to Date
|
||||||
resetPasswordExpiry: data.resetPasswordExpiry ? new Date(data.resetPasswordExpiry) : undefined,
|
resetPasswordExpiry: data.resetPasswordExpiry ? new Date(data.resetPasswordExpiry) : undefined,
|
||||||
|
// Convert lastPasswordChange from ISO string to Date
|
||||||
lastPasswordChange: new Date(data.lastPasswordChange),
|
lastPasswordChange: new Date(data.lastPasswordChange),
|
||||||
|
// Convert lockedUntil from ISO string to Date
|
||||||
lockedUntil: data.lockedUntil ? new Date(data.lockedUntil) : undefined,
|
lockedUntil: data.lockedUntil ? new Date(data.lockedUntil) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert BaseUser from API response, parsing date fields
|
* Convert BaseUser from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt, lastLogin
|
||||||
*/
|
*/
|
||||||
export function convertBaseUserFromApi(data: any): BaseUser {
|
export function convertBaseUserFromApi(data: any): BaseUser {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
|
// Convert lastLogin from ISO string to Date
|
||||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert BaseUserWithType from API response, parsing date fields
|
* Convert BaseUserWithType from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt, lastLogin
|
||||||
*/
|
*/
|
||||||
export function convertBaseUserWithTypeFromApi(data: any): BaseUserWithType {
|
export function convertBaseUserWithTypeFromApi(data: any): BaseUserWithType {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
|
// Convert lastLogin from ISO string to Date
|
||||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Candidate from API response, parsing date fields
|
* Convert Candidate from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt, lastLogin, availabilityDate
|
||||||
*/
|
*/
|
||||||
export function convertCandidateFromApi(data: any): Candidate {
|
export function convertCandidateFromApi(data: any): Candidate {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
|
// Convert lastLogin from ISO string to Date
|
||||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||||
userType: data.userType ? new Date(data.userType) : undefined,
|
// Convert availabilityDate from ISO string to Date
|
||||||
questions: data.questions ? new Date(data.questions) : undefined,
|
|
||||||
availabilityDate: data.availabilityDate ? new Date(data.availabilityDate) : undefined,
|
availabilityDate: data.availabilityDate ? new Date(data.availabilityDate) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Convert CandidateListResponse from API response, parsing date fields
|
|
||||||
*/
|
|
||||||
export function convertCandidateListResponseFromApi(data: any): CandidateListResponse {
|
|
||||||
if (!data) return data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
data: data.data ? new Date(data.data) : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Convert CandidateResponse from API response, parsing date fields
|
|
||||||
*/
|
|
||||||
export function convertCandidateResponseFromApi(data: any): CandidateResponse {
|
|
||||||
if (!data) return data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
data: data.data ? new Date(data.data) : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Convert Certification from API response, parsing date fields
|
* Convert Certification from API response, parsing date fields
|
||||||
|
* Date fields: issueDate, expirationDate
|
||||||
*/
|
*/
|
||||||
export function convertCertificationFromApi(data: any): Certification {
|
export function convertCertificationFromApi(data: any): Certification {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert issueDate from ISO string to Date
|
||||||
issueDate: new Date(data.issueDate),
|
issueDate: new Date(data.issueDate),
|
||||||
|
// Convert expirationDate from ISO string to Date
|
||||||
expirationDate: data.expirationDate ? new Date(data.expirationDate) : undefined,
|
expirationDate: data.expirationDate ? new Date(data.expirationDate) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Convert ChatContext from API response, parsing date fields
|
|
||||||
*/
|
|
||||||
export function convertChatContextFromApi(data: any): ChatContext {
|
|
||||||
if (!data) return data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
relatedEntityType: data.relatedEntityType ? new Date(data.relatedEntityType) : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Convert ChatMessage from API response, parsing date fields
|
* Convert ChatMessage from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertChatMessageFromApi(data: any): ChatMessage {
|
export function convertChatMessageFromApi(data: any): ChatMessage {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert ChatMessageBase from API response, parsing date fields
|
* Convert ChatMessageBase from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertChatMessageBaseFromApi(data: any): ChatMessageBase {
|
export function convertChatMessageBaseFromApi(data: any): ChatMessageBase {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert ChatMessageUser from API response, parsing date fields
|
* Convert ChatMessageUser from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertChatMessageUserFromApi(data: any): ChatMessageUser {
|
export function convertChatMessageUserFromApi(data: any): ChatMessageUser {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert ChatSession from API response, parsing date fields
|
* Convert ChatSession from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, lastActivity
|
||||||
*/
|
*/
|
||||||
export function convertChatSessionFromApi(data: any): ChatSession {
|
export function convertChatSessionFromApi(data: any): ChatSession {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||||
|
// Convert lastActivity from ISO string to Date
|
||||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert DataSourceConfiguration from API response, parsing date fields
|
* Convert DataSourceConfiguration from API response, parsing date fields
|
||||||
|
* Date fields: lastRefreshed
|
||||||
*/
|
*/
|
||||||
export function convertDataSourceConfigurationFromApi(data: any): DataSourceConfiguration {
|
export function convertDataSourceConfigurationFromApi(data: any): DataSourceConfiguration {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert lastRefreshed from ISO string to Date
|
||||||
lastRefreshed: data.lastRefreshed ? new Date(data.lastRefreshed) : undefined,
|
lastRefreshed: data.lastRefreshed ? new Date(data.lastRefreshed) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert EditHistory from API response, parsing date fields
|
* Convert EditHistory from API response, parsing date fields
|
||||||
|
* Date fields: editedAt
|
||||||
*/
|
*/
|
||||||
export function convertEditHistoryFromApi(data: any): EditHistory {
|
export function convertEditHistoryFromApi(data: any): EditHistory {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert editedAt from ISO string to Date
|
||||||
editedAt: new Date(data.editedAt),
|
editedAt: new Date(data.editedAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Education from API response, parsing date fields
|
* Convert Education from API response, parsing date fields
|
||||||
|
* Date fields: startDate, endDate
|
||||||
*/
|
*/
|
||||||
export function convertEducationFromApi(data: any): Education {
|
export function convertEducationFromApi(data: any): Education {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert startDate from ISO string to Date
|
||||||
startDate: new Date(data.startDate),
|
startDate: new Date(data.startDate),
|
||||||
|
// Convert endDate from ISO string to Date
|
||||||
endDate: data.endDate ? new Date(data.endDate) : undefined,
|
endDate: data.endDate ? new Date(data.endDate) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Employer from API response, parsing date fields
|
* Convert Employer from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt, lastLogin
|
||||||
*/
|
*/
|
||||||
export function convertEmployerFromApi(data: any): Employer {
|
export function convertEmployerFromApi(data: any): Employer {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
|
// Convert lastLogin from ISO string to Date
|
||||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Guest from API response, parsing date fields
|
* Convert Guest from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, lastActivity
|
||||||
*/
|
*/
|
||||||
export function convertGuestFromApi(data: any): Guest {
|
export function convertGuestFromApi(data: any): Guest {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert lastActivity from ISO string to Date
|
||||||
lastActivity: new Date(data.lastActivity),
|
lastActivity: new Date(data.lastActivity),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert InterviewFeedback from API response, parsing date fields
|
* Convert InterviewFeedback from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt
|
||||||
*/
|
*/
|
||||||
export function convertInterviewFeedbackFromApi(data: any): InterviewFeedback {
|
export function convertInterviewFeedbackFromApi(data: any): InterviewFeedback {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert InterviewSchedule from API response, parsing date fields
|
* Convert InterviewSchedule from API response, parsing date fields
|
||||||
|
* Date fields: scheduledDate, endDate
|
||||||
*/
|
*/
|
||||||
export function convertInterviewScheduleFromApi(data: any): InterviewSchedule {
|
export function convertInterviewScheduleFromApi(data: any): InterviewSchedule {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert scheduledDate from ISO string to Date
|
||||||
scheduledDate: new Date(data.scheduledDate),
|
scheduledDate: new Date(data.scheduledDate),
|
||||||
|
// Convert endDate from ISO string to Date
|
||||||
endDate: new Date(data.endDate),
|
endDate: new Date(data.endDate),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Job from API response, parsing date fields
|
* Convert Job from API response, parsing date fields
|
||||||
|
* Date fields: datePosted, applicationDeadline, featuredUntil
|
||||||
*/
|
*/
|
||||||
export function convertJobFromApi(data: any): Job {
|
export function convertJobFromApi(data: any): Job {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert datePosted from ISO string to Date
|
||||||
datePosted: new Date(data.datePosted),
|
datePosted: new Date(data.datePosted),
|
||||||
|
// Convert applicationDeadline from ISO string to Date
|
||||||
applicationDeadline: data.applicationDeadline ? new Date(data.applicationDeadline) : undefined,
|
applicationDeadline: data.applicationDeadline ? new Date(data.applicationDeadline) : undefined,
|
||||||
|
// Convert featuredUntil from ISO string to Date
|
||||||
featuredUntil: data.featuredUntil ? new Date(data.featuredUntil) : undefined,
|
featuredUntil: data.featuredUntil ? new Date(data.featuredUntil) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert JobApplication from API response, parsing date fields
|
* Convert JobApplication from API response, parsing date fields
|
||||||
|
* Date fields: appliedDate, updatedDate
|
||||||
*/
|
*/
|
||||||
export function convertJobApplicationFromApi(data: any): JobApplication {
|
export function convertJobApplicationFromApi(data: any): JobApplication {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert appliedDate from ISO string to Date
|
||||||
appliedDate: new Date(data.appliedDate),
|
appliedDate: new Date(data.appliedDate),
|
||||||
|
// Convert updatedDate from ISO string to Date
|
||||||
updatedDate: new Date(data.updatedDate),
|
updatedDate: new Date(data.updatedDate),
|
||||||
candidateContact: data.candidateContact ? new Date(data.candidateContact) : undefined,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert MessageReaction from API response, parsing date fields
|
* Convert MessageReaction from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertMessageReactionFromApi(data: any): MessageReaction {
|
export function convertMessageReactionFromApi(data: any): MessageReaction {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert RAGConfiguration from API response, parsing date fields
|
* Convert RAGConfiguration from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt
|
||||||
*/
|
*/
|
||||||
export function convertRAGConfigurationFromApi(data: any): RAGConfiguration {
|
export function convertRAGConfigurationFromApi(data: any): RAGConfiguration {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert RefreshToken from API response, parsing date fields
|
* Convert RefreshToken from API response, parsing date fields
|
||||||
|
* Date fields: expiresAt
|
||||||
*/
|
*/
|
||||||
export function convertRefreshTokenFromApi(data: any): RefreshToken {
|
export function convertRefreshTokenFromApi(data: any): RefreshToken {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert expiresAt from ISO string to Date
|
||||||
expiresAt: new Date(data.expiresAt),
|
expiresAt: new Date(data.expiresAt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert UserActivity from API response, parsing date fields
|
* Convert UserActivity from API response, parsing date fields
|
||||||
|
* Date fields: timestamp
|
||||||
*/
|
*/
|
||||||
export function convertUserActivityFromApi(data: any): UserActivity {
|
export function convertUserActivityFromApi(data: any): UserActivity {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert timestamp from ISO string to Date
|
||||||
timestamp: new Date(data.timestamp),
|
timestamp: new Date(data.timestamp),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert Viewer from API response, parsing date fields
|
* Convert Viewer from API response, parsing date fields
|
||||||
|
* Date fields: createdAt, updatedAt, lastLogin
|
||||||
*/
|
*/
|
||||||
export function convertViewerFromApi(data: any): Viewer {
|
export function convertViewerFromApi(data: any): Viewer {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert createdAt from ISO string to Date
|
||||||
createdAt: new Date(data.createdAt),
|
createdAt: new Date(data.createdAt),
|
||||||
|
// Convert updatedAt from ISO string to Date
|
||||||
updatedAt: new Date(data.updatedAt),
|
updatedAt: new Date(data.updatedAt),
|
||||||
|
// Convert lastLogin from ISO string to Date
|
||||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convert WorkExperience from API response, parsing date fields
|
* Convert WorkExperience from API response, parsing date fields
|
||||||
|
* Date fields: startDate, endDate
|
||||||
*/
|
*/
|
||||||
export function convertWorkExperienceFromApi(data: any): WorkExperience {
|
export function convertWorkExperienceFromApi(data: any): WorkExperience {
|
||||||
if (!data) return data;
|
if (!data) return data;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
// Convert startDate from ISO string to Date
|
||||||
startDate: new Date(data.startDate),
|
startDate: new Date(data.startDate),
|
||||||
|
// Convert endDate from ISO string to Date
|
||||||
endDate: data.endDate ? new Date(data.endDate) : undefined,
|
endDate: data.endDate ? new Date(data.endDate) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1094,8 +1124,6 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
|||||||
return convertApplicationDecisionFromApi(data) as T;
|
return convertApplicationDecisionFromApi(data) as T;
|
||||||
case 'Attachment':
|
case 'Attachment':
|
||||||
return convertAttachmentFromApi(data) as T;
|
return convertAttachmentFromApi(data) as T;
|
||||||
case 'AuthResponse':
|
|
||||||
return convertAuthResponseFromApi(data) as T;
|
|
||||||
case 'Authentication':
|
case 'Authentication':
|
||||||
return convertAuthenticationFromApi(data) as T;
|
return convertAuthenticationFromApi(data) as T;
|
||||||
case 'BaseUser':
|
case 'BaseUser':
|
||||||
@ -1104,14 +1132,8 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
|||||||
return convertBaseUserWithTypeFromApi(data) as T;
|
return convertBaseUserWithTypeFromApi(data) as T;
|
||||||
case 'Candidate':
|
case 'Candidate':
|
||||||
return convertCandidateFromApi(data) as T;
|
return convertCandidateFromApi(data) as T;
|
||||||
case 'CandidateListResponse':
|
|
||||||
return convertCandidateListResponseFromApi(data) as T;
|
|
||||||
case 'CandidateResponse':
|
|
||||||
return convertCandidateResponseFromApi(data) as T;
|
|
||||||
case 'Certification':
|
case 'Certification':
|
||||||
return convertCertificationFromApi(data) as T;
|
return convertCertificationFromApi(data) as T;
|
||||||
case 'ChatContext':
|
|
||||||
return convertChatContextFromApi(data) as T;
|
|
||||||
case 'ChatMessage':
|
case 'ChatMessage':
|
||||||
return convertChatMessageFromApi(data) as T;
|
return convertChatMessageFromApi(data) as T;
|
||||||
case 'ChatMessageBase':
|
case 'ChatMessageBase':
|
||||||
|
@ -101,10 +101,42 @@ def is_date_type(python_type: Any) -> bool:
|
|||||||
if python_type == datetime:
|
if python_type == datetime:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# String representation checks for various datetime types
|
# Check if it's a datetime type from the datetime module
|
||||||
|
if hasattr(python_type, '__module__') and hasattr(python_type, '__name__'):
|
||||||
|
module_name = getattr(python_type, '__module__', '')
|
||||||
|
type_name = getattr(python_type, '__name__', '')
|
||||||
|
|
||||||
|
# Check for datetime module types
|
||||||
|
if module_name == 'datetime' and type_name in ('datetime', 'date', 'time'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# String representation checks for specific datetime patterns (more restrictive)
|
||||||
type_str = str(python_type)
|
type_str = str(python_type)
|
||||||
date_patterns = ['datetime', 'date', 'DateTime', 'Date']
|
|
||||||
return any(pattern in type_str for pattern in date_patterns)
|
# Be very specific about datetime patterns to avoid false positives
|
||||||
|
specific_date_patterns = [
|
||||||
|
'datetime.datetime',
|
||||||
|
'datetime.date',
|
||||||
|
'datetime.time',
|
||||||
|
'<class \'datetime.datetime\'>',
|
||||||
|
'<class \'datetime.date\'>',
|
||||||
|
'<class \'datetime.time\'>',
|
||||||
|
'typing.DateTime',
|
||||||
|
'pydantic.datetime',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Check for exact matches or specific patterns
|
||||||
|
for pattern in specific_date_patterns:
|
||||||
|
if pattern in type_str:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Additional check for common datetime type aliases
|
||||||
|
if hasattr(python_type, '__origin__'):
|
||||||
|
origin_str = str(python_type.__origin__)
|
||||||
|
if 'datetime' in origin_str and 'datetime.' in origin_str:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def python_type_to_typescript(python_type: Any, debug: bool = False) -> str:
|
def python_type_to_typescript(python_type: Any, debug: bool = False) -> str:
|
||||||
"""Convert a Python type to TypeScript type string"""
|
"""Convert a Python type to TypeScript type string"""
|
||||||
@ -351,7 +383,11 @@ def process_pydantic_model(model_class, debug: bool = False) -> Dict[str, Any]:
|
|||||||
print(f" Raw type: {field_type}")
|
print(f" Raw type: {field_type}")
|
||||||
|
|
||||||
# Check if this is a date field
|
# Check if this is a date field
|
||||||
if is_date_type(field_type):
|
is_date = is_date_type(field_type)
|
||||||
|
if debug:
|
||||||
|
print(f" 📅 Date type check for {ts_name}: {is_date} (type: {field_type})")
|
||||||
|
|
||||||
|
if is_date:
|
||||||
is_optional = is_field_optional(field_info, field_type, debug)
|
is_optional = is_field_optional(field_info, field_type, debug)
|
||||||
date_fields.append({
|
date_fields.append({
|
||||||
'name': ts_name,
|
'name': ts_name,
|
||||||
@ -359,6 +395,8 @@ def process_pydantic_model(model_class, debug: bool = False) -> Dict[str, Any]:
|
|||||||
})
|
})
|
||||||
if debug:
|
if debug:
|
||||||
print(f" 🗓️ Date field detected: {ts_name} (optional: {is_optional})")
|
print(f" 🗓️ Date field detected: {ts_name} (optional: {is_optional})")
|
||||||
|
elif debug and ('date' in str(field_type).lower() or 'time' in str(field_type).lower()):
|
||||||
|
print(f" ⚠️ Field {ts_name} contains 'date'/'time' but not detected as date type: {field_type}")
|
||||||
|
|
||||||
ts_type = python_type_to_typescript(field_type, debug)
|
ts_type = python_type_to_typescript(field_type, debug)
|
||||||
|
|
||||||
@ -396,7 +434,11 @@ def process_pydantic_model(model_class, debug: bool = False) -> Dict[str, Any]:
|
|||||||
print(f" Raw type: {field_type}")
|
print(f" Raw type: {field_type}")
|
||||||
|
|
||||||
# Check if this is a date field
|
# Check if this is a date field
|
||||||
if is_date_type(field_type):
|
is_date = is_date_type(field_type)
|
||||||
|
if debug:
|
||||||
|
print(f" 📅 Date type check for {ts_name}: {is_date} (type: {field_type})")
|
||||||
|
|
||||||
|
if is_date:
|
||||||
is_optional = is_field_optional(field_info, field_type)
|
is_optional = is_field_optional(field_info, field_type)
|
||||||
date_fields.append({
|
date_fields.append({
|
||||||
'name': ts_name,
|
'name': ts_name,
|
||||||
@ -404,6 +446,8 @@ def process_pydantic_model(model_class, debug: bool = False) -> Dict[str, Any]:
|
|||||||
})
|
})
|
||||||
if debug:
|
if debug:
|
||||||
print(f" 🗓️ Date field detected: {ts_name} (optional: {is_optional})")
|
print(f" 🗓️ Date field detected: {ts_name} (optional: {is_optional})")
|
||||||
|
elif debug and ('date' in str(field_type).lower() or 'time' in str(field_type).lower()):
|
||||||
|
print(f" ⚠️ Field {ts_name} contains 'date'/'time' but not detected as date type: {field_type}")
|
||||||
|
|
||||||
ts_type = python_type_to_typescript(field_type, debug)
|
ts_type = python_type_to_typescript(field_type, debug)
|
||||||
|
|
||||||
@ -471,6 +515,7 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
|||||||
func_lines = [
|
func_lines = [
|
||||||
f"/**",
|
f"/**",
|
||||||
f" * Convert {interface_name} from API response, parsing date fields",
|
f" * Convert {interface_name} from API response, parsing date fields",
|
||||||
|
f" * Date fields: {', '.join([f['name'] for f in date_fields])}",
|
||||||
f" */",
|
f" */",
|
||||||
f"export function {function_name}(data: any): {interface_name} {{",
|
f"export function {function_name}(data: any): {interface_name} {{",
|
||||||
f" if (!data) return data;",
|
f" if (!data) return data;",
|
||||||
@ -479,11 +524,14 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
|||||||
f" ...data,"
|
f" ...data,"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add date field conversions
|
# Add date field conversions with validation
|
||||||
for date_field in date_fields:
|
for date_field in date_fields:
|
||||||
field_name = date_field['name']
|
field_name = date_field['name']
|
||||||
is_optional = date_field['optional']
|
is_optional = date_field['optional']
|
||||||
|
|
||||||
|
# Add a comment for clarity
|
||||||
|
func_lines.append(f" // Convert {field_name} from ISO string to Date")
|
||||||
|
|
||||||
if is_optional:
|
if is_optional:
|
||||||
func_lines.append(f" {field_name}: data.{field_name} ? new Date(data.{field_name}) : undefined,")
|
func_lines.append(f" {field_name}: data.{field_name} ? new Date(data.{field_name}) : undefined,")
|
||||||
else:
|
else:
|
||||||
@ -780,10 +828,27 @@ Generated conversion functions can be used like:
|
|||||||
file_size = len(ts_content)
|
file_size = len(ts_content)
|
||||||
print(f"✅ TypeScript types generated: {args.output} ({file_size} characters)")
|
print(f"✅ TypeScript types generated: {args.output} ({file_size} characters)")
|
||||||
|
|
||||||
# Count conversion functions
|
# Count conversion functions and provide detailed feedback
|
||||||
conversion_count = ts_content.count('export function convert') - ts_content.count('convertFromApi') - ts_content.count('convertArrayFromApi')
|
conversion_count = ts_content.count('export function convert') - ts_content.count('convertFromApi') - ts_content.count('convertArrayFromApi')
|
||||||
if conversion_count > 0:
|
if conversion_count > 0:
|
||||||
print(f"🗓️ Generated {conversion_count} date conversion functions")
|
print(f"🗓️ Generated {conversion_count} date conversion functions")
|
||||||
|
if args.debug:
|
||||||
|
# Show which models have date conversion
|
||||||
|
models_with_dates = []
|
||||||
|
for line in ts_content.split('\n'):
|
||||||
|
if line.startswith('export function convert') and 'FromApi' in line and 'convertFromApi' not in line:
|
||||||
|
model_name = line.split('convert')[1].split('FromApi')[0]
|
||||||
|
models_with_dates.append(model_name)
|
||||||
|
if models_with_dates:
|
||||||
|
print(f" Models with date conversion: {', '.join(models_with_dates)}")
|
||||||
|
|
||||||
|
# Provide troubleshooting info if debug mode
|
||||||
|
if args.debug:
|
||||||
|
print(f"\n🐛 Debug mode was enabled. If you see incorrect date conversions:")
|
||||||
|
print(f" 1. Check the debug output above for '📅 Date type check' lines")
|
||||||
|
print(f" 2. Look for '⚠️' warnings about false positives")
|
||||||
|
print(f" 3. Verify your Pydantic model field types are correct")
|
||||||
|
print(f" 4. Re-run with --debug to see detailed type analysis")
|
||||||
|
|
||||||
# Step 5: Compile TypeScript (unless skipped)
|
# Step 5: Compile TypeScript (unless skipped)
|
||||||
if not args.skip_compile:
|
if not args.skip_compile:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user