An internal AI-powered CV optimization tool that helps enhance resumes through analysis, optimization, and job description matching.
CV Branding Buddy integrates a React/TypeScript frontend with a Python backend deployed as a Google Cloud Function for advanced CV processing capabilities.
- CV Parser: Gemini 2.0-powered backend handles document parsing and AI-driven optimization
- Data Store: Supabase provides authentication, storage, and database services
- UI Framework: React 18 with shadcn/ui components and Tailwind CSS styling
The application follows a feature-based architecture with clear separation of concerns:
src/
├── components/ # Reusable UI components
│ ├── ui/ # shadcn/ui base components
│ └── ... # Custom application components
├── contexts/ # Global state management
│ ├── AuthContext.tsx # Authentication state
│ ├── CVContext.tsx # CV data management
│ ├── RecruiterContext.tsx # Recruiter profile data
│ └── SettingsContext.tsx # Application settings
├── hooks/ # Custom React hooks
├── integrations/ # Third-party service integrations
│ └── supabase/ # Supabase client and services
├── lib/ # Utility functions and helpers
├── pages/ # Main application routes
│ ├── Auth/ # Authentication pages
│ ├── Upload.tsx # CV upload page
│ ├── Preview.tsx # CV preview and editing
│ ├── Profile.tsx # User profile management
│ └── ... # Other application pages
├── services/ # API and business logic
│ ├── api.ts # High-level API interface
│ ├── cvParserApi.ts # CV parser backend integration
│ └── cvService.ts # CV data service
└── types/ # TypeScript type definitions
└── cv.ts # CV data model types
The backend consists of a Python-based Google Cloud Function that provides CV parsing and optimization capabilities:
cv-optimizer/
├── main.py # Entry point for the Cloud Function
├── requirements.txt # Python dependencies
├── utils/ # Utility functions
│ ├── parser.py # CV parsing logic
│ ├── optimizer.py # CV optimization logic
│ └── formatter.py # Output formatting
└── models/ # Data models
├── cv.py # CV data model
└── response.py # API response model
The backend exposes the following HTTP endpoints:
| Endpoint | Method | Description | Parameters |
|---|---|---|---|
/parse |
POST | Parse CV document | cv_file, jd_file (optional) |
/optimize/profile |
POST | Optimize profile statement | cv_data, job_description (optional) |
/optimize/skills |
POST | Optimize skills section | cv_data, job_description (optional) |
/optimize/achievements |
POST | Optimize achievements | cv_data, job_description (optional) |
/optimize/experience |
POST | Optimize experience section | cv_data, experience_index, job_description (optional) |
/score |
POST | Score CV against job description | cv_data, job_description |
- Google Cloud Function for CV parsing and optimization
- Communication via HTTP endpoints using Axios
- Authentication with Supabase JWT tokens
- File uploads handled via multipart/form-data
- User uploads CV (and optional job description)
- Frontend saves file to Supabase storage
- File is sent to CV Parser backend
- Backend extracts structured data and returns JSON
- Frontend updates CV context with parsed data
- User can edit, optimize, and generate documents
Key interfaces in src/types/cv.ts:
export interface CV {
id?: string;
file?: File;
jobDescription?: string;
firstName: string | null;
surname: string | null;
email: string | null;
phone: string | null;
links: Link[] | null;
location: Location | null;
headline: string;
profileStatement: string;
skills: Skill[];
achievements: string[];
languages: Language[] | null;
experience: Experience[];
education: Education[] | null;
certifications: Certification[] | null;
professionalMemberships: ProfessionalMembership[] | null;
earlierCareer: EarlierCareer[] | null;
publications: Publication[] | null;
addDetails: string[] | null;
}-
CVContext: Manages CV data state and operations
cv: Current CV dataupdateCvField: Updates specific CV fieldssectionVisibility: Controls section displaysectionOrder: Manages section orderingisAnonymised: Toggles PII anonymization
-
AuthContext: Handles authentication
user: Current user datasignIn,signUp,signOut: Auth methodsisLoading: Authentication state
The application uses React Context API for global state management with a clear hierarchy:
App
├── AuthProvider (authentication state)
│ ├── CVProvider (CV data and operations)
│ │ ├── RecruiterProvider (recruiter profile)
│ │ │ └── SettingsProvider (application settings)
│ │ │ └── Application Components
│ │ └── Application Components
│ └── Application Components
└── Application Components
// Example of state flow in a component
import { useAuth } from '@/contexts/AuthContext';
import { useCVContext } from '@/contexts/CVContext';
import { useSettingsContext } from '@/contexts/SettingsContext';
const ProfileSection = () => {
const { user } = useAuth();
const { cv, updateCvField } = useCVContext();
const { settings } = useSettingsContext();
const handleUpdateProfile = (newProfile) => {
// Update CV data
updateCvField('profileStatement', newProfile);
// Save to backend
saveToBackend(cv.id, newProfile);
};
return (
// Component JSX
);
};The application uses Supabase for authentication with the following flow:
-
User Registration/Sign In:
- User enters credentials on the Auth pages
- Supabase Auth API handles authentication
- JWT token is stored in localStorage
-
Session Management:
AuthContextprovides the current user stateProtectedRoutecomponent ensures authenticated access- Token refresh is handled automatically by Supabase client
-
API Authentication:
- All API requests include the JWT token in the Authorization header
- Backend validates the token with Supabase
// Example of authentication in API requests
import { supabase } from '@/integrations/supabase/client';
const makeAuthenticatedRequest = async (endpoint, data) => {
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
throw new Error('Not authenticated');
}
return axios.post(endpoint, data, {
headers: {
Authorization: `Bearer ${session.access_token}`
}
});
};src/services/api.ts provides the following core methods:
uploadCV(file, jdFile): Upload and parse CVoptimizeProfileStatement(cv, jobDescription): Enhance profile statementoptimizeSkills(cv, jobDescription): Optimize skills sectionoptimizeAchievements(cv, jobDescription): Improve achievementsoptimizeExperience(cv, experienceIndex, jobDescription): Enhance role descriptionsgenerateDocument(cv, format, recruiterProfile): Create formatted document
import { uploadCV, optimizeProfileStatement } from '@/services/api';
import { useCVContext } from '@/contexts/CVContext';
const UploadComponent = () => {
const { setCv, setIsLoading } = useCVContext();
const handleUpload = async (file) => {
setIsLoading(true);
try {
const response = await uploadCV(file);
if (response.status === 'success') {
setCv(response.data);
} else {
throw new Error(response.errors?.[0] || 'Upload failed');
}
} catch (error) {
console.error('Upload error:', error);
// Handle error (show toast, etc.)
} finally {
setIsLoading(false);
}
};
return (
// Component JSX
);
};The application implements a comprehensive error handling strategy:
// Example of API error handling
const handleApiRequest = async () => {
try {
const response = await apiService.makeRequest();
if (response.status === 'error') {
// Handle API-level errors
throw new Error(response.errors?.[0] || 'Unknown API error');
}
return response.data;
} catch (error) {
// Handle network errors, validation errors, etc.
if (error.response) {
// Server responded with error status
handleServerError(error.response);
} else if (error.request) {
// Request made but no response
handleNetworkError();
} else {
// Other errors
handleGeneralError(error);
}
// Re-throw or handle as needed
throw error;
}
};- Toast notifications for user-facing errors
- Form validation with React Hook Form and Zod
- Loading states for async operations
- Fallback UI components for error states
- Client-side file size validation (max 5MB)
- Chunked uploads for large files
- Compression for image uploads
- Memoization of expensive components with React.memo
- Virtualized lists for long CV sections
- Lazy loading of non-critical components
- Caching of API responses with React Query
- Debouncing of frequent API calls
- Optimistic updates for better UX
- Node.js 18+ and npm/yarn
- Supabase account and project
- Access to GCP project for backend
- Python 3.9+ for local backend development
Required variables in .env:
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
VITE_CV_OPTIMIZER_GCF_URL=https://your-region-your-project.cloudfunctions.net/cv_optimizer
# Install dependencies
npm install
# Start development server
npm run dev# Clone the backend repository
git clone https://github.com/your-org/cv-optimizer-backend.git
cd cv-optimizer-backend
# Install Python dependencies
pip install -r requirements.txt
# Set up environment variables
cp .env.example .env
# Edit .env with your configuration
# Run locally
python -m functions-framework --target=cv_optimizer --debugTests use Jest and React Testing Library. Key test files:
src/__tests__/: Application testssrc/services/__tests__/: API service tests
- Unit Tests: Test individual components and functions
- Integration Tests: Test interactions between components
- API Tests: Test API service functions with mocks
- E2E Tests: Test complete user flows (coming soon)
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { CVProvider } from '@/contexts/CVContext';
import { UploadPage } from '@/pages/Upload';
// Mock API service
jest.mock('@/services/api', () => ({
uploadCV: jest.fn().mockResolvedValue({
status: 'success',
data: { /* mock CV data */ }
})
}));
describe('UploadPage', () => {
it('should handle file upload successfully', async () => {
render(
<CVProvider>
<UploadPage />
</CVProvider>
);
// Find file input and simulate file selection
const fileInput = screen.getByLabelText(/upload cv/i);
const file = new File(['dummy content'], 'test.pdf', { type: 'application/pdf' });
fireEvent.change(fileInput, { target: { files: [file] } });
// Click upload button
const uploadButton = screen.getByText(/upload cv/i);
fireEvent.click(uploadButton);
// Wait for success message
await waitFor(() => {
expect(screen.getByText(/cv uploaded successfully/i)).toBeInTheDocument();
});
});
});Run tests with:
npm run test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage report-
Build the project for production:
npm run build
-
Deploy to your hosting platform:
# For Netlify netlify deploy --prod # For Vercel vercel --prod
-
Configure environment variables on your hosting platform.
-
Deploy to Google Cloud Functions:
gcloud functions deploy cv_optimizer \ --runtime python39 \ --trigger-http \ --allow-unauthenticated \ --entry-point cv_optimizer
-
Update the frontend environment variable with the new function URL.
- Development: Local development environment
- Staging: Pre-production environment for testing
- Production: Live environment for end users
- Update CV type definition in
src/types/cv.ts - Modify CVContext if needed for new state management
- Update API services in
services/api.tsandservices/cvParserApi.ts - Implement UI components for the new features
- Add tests for new functionality
The app uses Supabase for:
- Authentication via
AuthContext - Storage for CV files and profile images
- Database for CV and user data
Tables defined in Supabase:
candidates: Person whose CV is being processedcvs: CV records including metadata and parsed dataprofiles: User profile information
- Problem: User can't log in
- Solution: Check Supabase project settings and ensure authentication is enabled for the email provider
- Problem: CV upload fails with "File too large"
- Solution: Check file size limit in Supabase storage settings (default is 5MB)
- Problem: Backend API calls fail
- Solution: Verify the
VITE_CV_OPTIMIZER_GCF_URLenvironment variable is correct
- Problem: Slow rendering with large CVs
- Solution: Implement virtualization for long lists and optimize component rendering
- React Developer Tools for component inspection
- Network tab in browser DevTools for API requests
- Supabase Dashboard for database inspection
- Google Cloud Logging for backend logs
- File size upload limit: 5MB
- Processing time varies by CV complexity
- Only PDF and DOCX files supported
- Limited job description matching for certain industries