feat: Complete Z.CRM system with all 6 modules
✨ Features: - Complete authentication system with JWT - Dashboard with all 6 modules visible - Contact Management module (Salesforce-style) - CRM & Sales Pipeline module (Pipedrive-style) - Inventory & Assets module (SAP-style) - Tasks & Projects module (Jira/Asana-style) - HR Management module (BambooHR-style) - Marketing Management module (HubSpot-style) - Admin Panel with user management and role matrix - World-class UI/UX with RTL Arabic support - Cairo font (headings) + Readex Pro font (body) - Sample data for all modules - Protected routes and authentication flow - Backend API with Prisma + PostgreSQL - Comprehensive documentation 🎨 Design: - Color-coded modules - Professional data tables - Stats cards with metrics - Progress bars and status badges - Search and filters - Responsive layout 📊 Tech Stack: - Frontend: Next.js 14, TypeScript, Tailwind CSS - Backend: Node.js, Express, Prisma - Database: PostgreSQL - Auth: JWT with bcrypt 🚀 Production-ready frontend with all features accessible
This commit is contained in:
148
frontend/src/lib/api.ts
Normal file
148
frontend/src/lib/api.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:5001/api/v1'
|
||||
|
||||
export const api = axios.create({
|
||||
baseURL: API_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
// Request interceptor to add auth token
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// Response interceptor to handle errors
|
||||
api.interceptors.response.use(
|
||||
(response) => response,
|
||||
async (error) => {
|
||||
const originalRequest = error.config
|
||||
|
||||
// If token expired, try to refresh
|
||||
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||
originalRequest._retry = true
|
||||
|
||||
try {
|
||||
const refreshToken = localStorage.getItem('refreshToken')
|
||||
const response = await axios.post(`${API_URL}/auth/refresh`, {
|
||||
refreshToken,
|
||||
})
|
||||
|
||||
const { accessToken } = response.data.data
|
||||
localStorage.setItem('accessToken', accessToken)
|
||||
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`
|
||||
return api(originalRequest)
|
||||
} catch (refreshError) {
|
||||
// Refresh failed, logout user
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('refreshToken')
|
||||
window.location.href = '/login'
|
||||
return Promise.reject(refreshError)
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// API Methods
|
||||
export const authAPI = {
|
||||
login: (email: string, password: string) =>
|
||||
api.post('/auth/login', { email, password }),
|
||||
register: (data: any) => api.post('/auth/register', data),
|
||||
logout: () => api.post('/auth/logout'),
|
||||
getProfile: () => api.get('/auth/me'),
|
||||
}
|
||||
|
||||
export const contactsAPI = {
|
||||
getAll: (params?: any) => api.get('/contacts', { params }),
|
||||
getById: (id: string) => api.get(`/contacts/${id}`),
|
||||
create: (data: any) => api.post('/contacts', data),
|
||||
update: (id: string, data: any) => api.put(`/contacts/${id}`, data),
|
||||
delete: (id: string, reason: string) =>
|
||||
api.delete(`/contacts/${id}`, { data: { reason } }),
|
||||
merge: (sourceId: string, targetId: string, reason: string) =>
|
||||
api.post('/contacts/merge', { sourceId, targetId, reason }),
|
||||
}
|
||||
|
||||
export const crmAPI = {
|
||||
// Deals
|
||||
getDeals: (params?: any) => api.get('/crm/deals', { params }),
|
||||
getDealById: (id: string) => api.get(`/crm/deals/${id}`),
|
||||
createDeal: (data: any) => api.post('/crm/deals', data),
|
||||
updateDeal: (id: string, data: any) => api.put(`/crm/deals/${id}`, data),
|
||||
winDeal: (id: string, actualValue: number, wonReason: string) =>
|
||||
api.post(`/crm/deals/${id}/win`, { actualValue, wonReason }),
|
||||
loseDeal: (id: string, lostReason: string) =>
|
||||
api.post(`/crm/deals/${id}/lose`, { lostReason }),
|
||||
|
||||
// Quotes
|
||||
getQuotes: (dealId: string) => api.get(`/crm/deals/${dealId}/quotes`),
|
||||
createQuote: (data: any) => api.post('/crm/quotes', data),
|
||||
approveQuote: (id: string) => api.post(`/crm/quotes/${id}/approve`),
|
||||
sendQuote: (id: string) => api.post(`/crm/quotes/${id}/send`),
|
||||
}
|
||||
|
||||
export const hrAPI = {
|
||||
getEmployees: (params?: any) => api.get('/hr/employees', { params }),
|
||||
getEmployeeById: (id: string) => api.get(`/hr/employees/${id}`),
|
||||
createEmployee: (data: any) => api.post('/hr/employees', data),
|
||||
updateEmployee: (id: string, data: any) => api.put(`/hr/employees/${id}`, data),
|
||||
terminateEmployee: (id: string, terminationDate: Date, reason: string) =>
|
||||
api.post(`/hr/employees/${id}/terminate`, { terminationDate, reason }),
|
||||
|
||||
// Attendance
|
||||
getAttendance: (employeeId: string, month: number, year: number) =>
|
||||
api.get(`/hr/attendance/${employeeId}`, { params: { month, year } }),
|
||||
recordAttendance: (data: any) => api.post('/hr/attendance', data),
|
||||
|
||||
// Leaves
|
||||
createLeaveRequest: (data: any) => api.post('/hr/leaves', data),
|
||||
approveLeave: (id: string) => api.post(`/hr/leaves/${id}/approve`),
|
||||
|
||||
// Salaries
|
||||
processSalary: (employeeId: string, month: number, year: number) =>
|
||||
api.post('/hr/salaries/process', { employeeId, month, year }),
|
||||
}
|
||||
|
||||
export const inventoryAPI = {
|
||||
getProducts: (params?: any) => api.get('/inventory/products', { params }),
|
||||
createProduct: (data: any) => api.post('/inventory/products', data),
|
||||
getWarehouses: () => api.get('/inventory/warehouses'),
|
||||
createWarehouse: (data: any) => api.post('/inventory/warehouses', data),
|
||||
getAssets: () => api.get('/inventory/assets'),
|
||||
createAsset: (data: any) => api.post('/inventory/assets', data),
|
||||
}
|
||||
|
||||
export const projectsAPI = {
|
||||
getProjects: (params?: any) => api.get('/projects/projects', { params }),
|
||||
getProjectById: (id: string) => api.get(`/projects/projects/${id}`),
|
||||
createProject: (data: any) => api.post('/projects/projects', data),
|
||||
updateProject: (id: string, data: any) => api.put(`/projects/projects/${id}`, data),
|
||||
|
||||
getTasks: (params?: any) => api.get('/projects/tasks', { params }),
|
||||
createTask: (data: any) => api.post('/projects/tasks', data),
|
||||
updateTask: (id: string, data: any) => api.put(`/projects/tasks/${id}`, data),
|
||||
}
|
||||
|
||||
export const marketingAPI = {
|
||||
getCampaigns: (params?: any) => api.get('/marketing/campaigns', { params }),
|
||||
getCampaignById: (id: string) => api.get(`/marketing/campaigns/${id}`),
|
||||
createCampaign: (data: any) => api.post('/marketing/campaigns', data),
|
||||
updateCampaign: (id: string, data: any) => api.put(`/marketing/campaigns/${id}`, data),
|
||||
approveCampaign: (id: string) => api.post(`/marketing/campaigns/${id}/approve`),
|
||||
launchCampaign: (id: string) => api.post(`/marketing/campaigns/${id}/launch`),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user