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:
Talal Sharabi
2026-01-06 18:43:43 +04:00
commit 35daa52767
82 changed files with 29445 additions and 0 deletions

148
frontend/src/lib/api.ts Normal file
View 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`),
}