Expert guidance for developing and maintaining a full-stack ATS built with React, Node.js/Express, MySQL, and modern UI components.
You are an expert full-stack developer working on an Applicant Tracking System (ATS) built with React frontend, Node.js/Express backend, and MySQL database. Follow these instructions carefully when making changes to this codebase.
This is a full-stack ATS managing job requisitions, candidates, applications, interviews, and complete hiring workflows.
```
Hkthon_ATS_latest_V5.24/
├── backend/ # Node.js/Express API (CommonJS)
│ ├── src/
│ │ ├── config/ # MySQL + Swagger config
│ │ ├── controllers/ # Request handlers
│ │ ├── middleware/ # Auth, validation, error handling
│ │ ├── models/ # Data models
│ │ ├── routes/ # API routes
│ │ ├── utils/ # Utilities
│ │ └── server.js # Express server
│ ├── scripts/ # DB seeding
│ └── package.json
├── frontend/ # React app (ES modules)
│ ├── src/
│ │ ├── components/ # shadcn/ui components
│ │ ├── pages/ # Page components
│ │ ├── services/ # Axios API layer
│ │ ├── store/ # Redux Toolkit slices
│ │ ├── contexts/ # React contexts
│ │ ├── hooks/ # Custom hooks
│ │ └── main.jsx
│ └── package.json
└── package.json # Root scripts
```
**Backend:**
**Frontend:**
**If ports are blocked:** Run `npx kill-port 3000 5000` before starting.
**Backend (`.env`):**
```env
PORT=5000
NODE_ENV=development
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=root
DB_NAME=ats_db
DB_PORT=3306
FRONTEND_URL=http://localhost:3000
API_VERSION=v1
```
**Frontend (`.env`):**
```env
VITE_API_URL=http://localhost:5000/api/v1
VITE_APP_NAME=ATS - Applicant Tracking System
VITE_APP_VERSION=1.0.0
```
```bash
npm run install:all # Install both backend + frontend
npm run install:backend # Backend only
npm run install:frontend # Frontend only
```
```bash
npm run dev # Run both servers concurrently
npm run dev:backend # Backend only (nodemon on port 5000)
npm run dev:frontend # Frontend only (Vite on port 3000)
```
```bash
npm run build # Build frontend
npm start # Start backend in production
```
```bash
npm run seed:job-requisitions # Seed jobs
npm run clear:job-requisitions # Clear jobs
```
```bash
cd frontend && npm run lint # ESLint check
cd frontend && npm run lint:fix # Auto-fix
```
**Database Name:** `ats_db`
**Main Tables:**
1. `users` - System users (roles: admin, hr_manager, recruiter, interviewer, hiring_manager)
2. `jobRequisitions` - Job postings
3. `candidates` - Profiles with `candidate_code` (CAND-XXX)
4. `applications` - Applications with `application_code` (APPL-XXX), status enum
5. `interviews` - Interviews with `scheduledDate` (DATETIME)
6. `auditLogs` - Audit trail
**Key Schema Rules:**
**Always convert JavaScript Date to MySQL DATETIME format:**
```javascript
const scheduledDate = new Date(formData.scheduledDate);
const mysqlDate = scheduledDate.toISOString().slice(0, 19).replace('T', ' ');
// Result: "2024-09-30 14:30:00"
```
**Frontend must send:**
```javascript
{
applicationId: "uuid",
interviewType: "phone", // enum value
scheduledDate: "2024-09-30 14:30:00", // MySQL DATETIME
duration: 60,
location: "Zoom link or address",
interviewerId: "uuid", // single string
notes: "Optional notes"
}
```
**Always await database calls in model methods:**
```javascript
static async getInterviewsWithDetails() {
const connection = await pool.getConnection();
try {
const [rows] = await connection.query(`...`);
return rows.map(row => ({
...row,
// Handle null values explicitly
candidateName: row.candidateName || 'Unknown',
jobTitle: row.jobTitle || 'N/A'
}));
} finally {
connection.release();
}
}
```
**Validation rules in `backend/src/middleware/validation.js` must match:**
**Frontend forms must submit data matching backend expectations.**
**Pass query params for tab navigation:**
```javascript
// From InterviewsPage to ApplicationDetailsPage
navigate(`/applications/${applicationId}?tab=interviews`);
// In ApplicationDetailsPage
const searchParams = new URLSearchParams(location.search);
const initialTab = searchParams.get('tab') || 'overview';
```
**Centralize API calls in `frontend/src/services/`:**
```javascript
import axiosInstance from './api';
export const interviewService = {
createInterview: (data) => axiosInstance.post('/interviews', data),
getInterviews: () => axiosInstance.get('/interviews'),
getInterview: (id) => axiosInstance.get(`/interviews/${id}`)
};
```
**Backend controllers:**
```javascript
try {
const result = await Model.method();
res.status(200).json({ success: true, data: result });
} catch (error) {
console.error('Error:', error);
res.status(500).json({
success: false,
message: 'Internal server error',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
```
**Frontend components:**
```javascript
try {
const response = await interviewService.createInterview(data);
toast.success('Interview scheduled successfully');
navigate('/interviews');
} catch (error) {
console.error('Failed to schedule interview:', error);
toast.error(error.response?.data?.message || 'Failed to schedule interview');
}
```
**Use Redux Toolkit slices for global state:**
```javascript
// store/interviewSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchInterviews = createAsyncThunk(
'interviews/fetchAll',
async () => {
const response = await interviewService.getInterviews();
return response.data;
}
);
const interviewSlice = createSlice({
name: 'interviews',
initialState: { items: [], loading: false, error: null },
extraReducers: (builder) => {
builder
.addCase(fetchInterviews.pending, (state) => { state.loading = true; })
.addCase(fetchInterviews.fulfilled, (state, action) => {
state.loading = false;
state.items = action.payload;
})
.addCase(fetchInterviews.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
```
**Define schemas for React Hook Form:**
```javascript
import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
const interviewSchema = z.object({
interviewType: z.enum(['phone', 'video', 'in-person', 'technical', 'panel']),
scheduledDate: z.string().min(1, 'Date is required'),
duration: z.number().min(15, 'Minimum 15 minutes'),
interviewerId: z.string().uuid('Invalid interviewer ID')
});
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(interviewSchema)
});
```
When adding a new feature:
1. **Database Changes:**
- [ ] Update `backend/src/models/` with new model or methods
- [ ] Add migration script if schema changes required
- [ ] Update seed scripts if needed
2. **Backend API:**
- [ ] Create/update controller in `backend/src/controllers/`
- [ ] Add validation rules in `backend/src/middleware/validation.js`
- [ ] Define routes in `backend/src/routes/`
- [ ] Add Swagger documentation comments
- [ ] Test with Postman or curl
3. **Frontend Service Layer:**
- [ ] Add API methods to `frontend/src/services/`
- [ ] Handle errors with try-catch and toast notifications
4. **State Management:**
- [ ] Create Redux slice if global state needed (`frontend/src/store/`)
- [ ] Define async thunks for API calls
5. **UI Components:**
- [ ] Create page component in `frontend/src/pages/`
- [ ] Use shadcn/ui components for consistency
- [ ] Add form validation with React Hook Form + Zod
- [ ] Implement loading and error states
- [ ] Add navigation links
6. **Testing:**
- [ ] Test all CRUD operations
- [ ] Verify validation rules on frontend and backend
- [ ] Check error handling and edge cases
- [ ] Test navigation flow
**Solution:**
```bash
npx kill-port 3000 5000
npm run dev
```
**Check:**
**Fix:**
**Fix:**
```javascript
candidateName: row.candidateName || 'Unknown'
```
**Fix:**
1. **Code Style:**
- Functional components with hooks (no class components)
- Async/await over promises
- ES6+ syntax (destructuring, arrow functions, template literals)
2. **State Management:**
- Redux for global state (user auth, app-wide data)
- Local state for UI-only concerns (form inputs, modals)
- React Query or Redux Toolkit Query for server state (future enhancement)
3. **Component Organization:**
- Keep components small and focused
- Extract reusable UI into `frontend/src/components/`
- Page components in `frontend/src/pages/`
4. **API Design:**
- RESTful conventions (GET, POST, PUT, DELETE)
- Consistent response format: `{ success: boolean, data?: any, message?: string }`
- Proper HTTP status codes
5. **Error Handling:**
- Specific error messages for validation failures
- Generic messages for server errors in production
- Log detailed errors server-side
6. **Security:**
- Never expose sensitive data in API responses
- Validate all inputs on both client and server
- Use parameterized queries to prevent SQL injection
- Keep JWT secret secure and rotate periodically
1. **Read the relevant code first** - Understand existing patterns before modifying
2. **Check database schema** - Ensure changes align with MySQL structure
3. **Update validation rules** - Keep frontend and backend validation in sync
4. **Test thoroughly** - Verify both happy path and error cases
5. **Update documentation** - Keep CLAUDE.md and comments current
6. **Commit frequently** - Small, focused commits with clear messages
**Backend Models:**
**Backend Controllers:**
**Frontend Pages:**
**Frontend Services:**
When in doubt, refer to working implementations in the codebase and this guide. Happy coding!
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/ats-applicant-tracking-system-guide/raw