fix email issues, add company,project,todos

This commit is contained in:
richardtekula
2025-11-21 13:56:02 +01:00
parent bb851639b8
commit ca93b6f2d2
30 changed files with 4860 additions and 1066 deletions

View File

@@ -0,0 +1,223 @@
import * as companyService from '../services/company.service.js';
import * as noteService from '../services/note.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/**
* Get all companies
* GET /api/companies?search=query
*/
export const getAllCompanies = async (req, res) => {
try {
const { search } = req.query;
const companies = await companyService.getAllCompanies(search);
res.status(200).json({
success: true,
count: companies.length,
data: companies,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get company by ID
* GET /api/companies/:companyId
*/
export const getCompanyById = async (req, res) => {
try {
const { companyId } = req.params;
const company = await companyService.getCompanyById(companyId);
res.status(200).json({
success: true,
data: company,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get company with relations (projects, todos, notes)
* GET /api/companies/:companyId/details
*/
export const getCompanyWithRelations = async (req, res) => {
try {
const { companyId } = req.params;
const company = await companyService.getCompanyWithRelations(companyId);
res.status(200).json({
success: true,
data: company,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create new company
* POST /api/companies
* Body: { name, description, address, city, country, phone, email, website }
*/
export const createCompany = async (req, res) => {
try {
const userId = req.userId;
const data = req.body;
const company = await companyService.createCompany(userId, data);
res.status(201).json({
success: true,
data: company,
message: 'Firma bola vytvorená',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update company
* PATCH /api/companies/:companyId
* Body: { name, description, address, city, country, phone, email, website }
*/
export const updateCompany = async (req, res) => {
try {
const { companyId } = req.params;
const data = req.body;
const company = await companyService.updateCompany(companyId, data);
res.status(200).json({
success: true,
data: company,
message: 'Firma bola aktualizovaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete company
* DELETE /api/companies/:companyId
*/
export const deleteCompany = async (req, res) => {
try {
const { companyId } = req.params;
const result = await companyService.deleteCompany(companyId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get company notes
* GET /api/companies/:companyId/notes
*/
export const getCompanyNotes = async (req, res) => {
try {
const { companyId } = req.params;
const notes = await noteService.getNotesByCompanyId(companyId);
res.status(200).json({
success: true,
count: notes.length,
data: notes,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Add company note
* POST /api/companies/:companyId/notes
*/
export const addCompanyNote = async (req, res) => {
try {
const userId = req.userId;
const { companyId } = req.params;
const { content, reminderAt } = req.body;
const note = await noteService.createNote(userId, {
content,
companyId,
reminderDate: reminderAt, // Map reminderAt to reminderDate
});
res.status(201).json({
success: true,
data: note,
message: 'Poznámka bola pridaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update company note
* PATCH /api/companies/:companyId/notes/:noteId
*/
export const updateCompanyNote = async (req, res) => {
try {
const { noteId } = req.params;
const { content, reminderAt } = req.body;
const note = await noteService.updateNote(noteId, {
content,
reminderDate: reminderAt, // Map reminderAt to reminderDate
});
res.status(200).json({
success: true,
data: note,
message: 'Poznámka bola aktualizovaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete company note
* DELETE /api/companies/:companyId/notes/:noteId
*/
export const deleteCompanyNote = async (req, res) => {
try {
const { noteId } = req.params;
const result = await noteService.deleteNote(noteId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};

View File

@@ -238,3 +238,125 @@ export const updateContact = async (req, res) => {
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Link company to contact
* POST /api/contacts/:contactId/link-company?accountId=xxx
* Body: { companyId }
*/
export const linkCompanyToContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
const { companyId } = req.body;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
if (!companyId) {
return res.status(400).json({
success: false,
error: {
message: 'companyId je povinný',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const updated = await contactService.linkCompanyToContact(contactId, accountId, companyId);
res.status(200).json({
success: true,
data: updated,
message: 'Firma bola linknutá ku kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Unlink company from contact
* POST /api/contacts/:contactId/unlink-company?accountId=xxx
*/
export const unlinkCompanyFromContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const updated = await contactService.unlinkCompanyFromContact(contactId, accountId);
res.status(200).json({
success: true,
data: updated,
message: 'Firma bola odlinknutá od kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create company from contact
* POST /api/contacts/:contactId/create-company?accountId=xxx
* Body: { name, email, phone, address, city, country, website, description } (all optional, uses contact data as defaults)
*/
export const createCompanyFromContact = async (req, res) => {
try {
const userId = req.userId;
const { contactId } = req.params;
const { accountId } = req.query;
const companyData = req.body;
if (!accountId) {
return res.status(400).json({
success: false,
error: {
message: 'accountId je povinný parameter',
statusCode: 400,
},
});
}
// Verify user has access to this email account
await emailAccountService.getEmailAccountById(accountId, userId);
const result = await contactService.createCompanyFromContact(contactId, accountId, userId, companyData);
res.status(201).json({
success: true,
data: result,
message: 'Firma bola vytvorená z kontaktu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};

View File

@@ -0,0 +1,159 @@
import * as noteService from '../services/note.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/**
* Get all notes
* GET /api/notes?search=query&companyId=xxx&projectId=xxx&todoId=xxx&contactId=xxx
*/
export const getAllNotes = async (req, res) => {
try {
const { search, companyId, projectId, todoId, contactId } = req.query;
const filters = {
searchTerm: search,
companyId,
projectId,
todoId,
contactId,
};
const notes = await noteService.getAllNotes(filters);
res.status(200).json({
success: true,
count: notes.length,
data: notes,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get note by ID
* GET /api/notes/:noteId
*/
export const getNoteById = async (req, res) => {
try {
const { noteId } = req.params;
const note = await noteService.getNoteById(noteId);
res.status(200).json({
success: true,
data: note,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create new note
* POST /api/notes
* Body: { title, content, companyId, projectId, todoId, contactId }
*/
export const createNote = async (req, res) => {
try {
const userId = req.userId;
const data = req.body;
const note = await noteService.createNote(userId, data);
res.status(201).json({
success: true,
data: note,
message: 'Poznámka bola vytvorená',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update note
* PATCH /api/notes/:noteId
* Body: { title, content, companyId, projectId, todoId, contactId }
*/
export const updateNote = async (req, res) => {
try {
const { noteId } = req.params;
const data = req.body;
const note = await noteService.updateNote(noteId, data);
res.status(200).json({
success: true,
data: note,
message: 'Poznámka bola aktualizovaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete note
* DELETE /api/notes/:noteId
*/
export const deleteNote = async (req, res) => {
try {
const { noteId } = req.params;
const result = await noteService.deleteNote(noteId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get upcoming reminders for current user
* GET /api/notes/my-reminders
*/
export const getMyReminders = async (req, res) => {
try {
const userId = req.userId;
const reminders = await noteService.getUpcomingRemindersForUser(userId);
res.status(200).json({
success: true,
count: reminders.length,
data: reminders,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Mark reminder as sent
* POST /api/notes/:noteId/mark-reminder-sent
*/
export const markReminderSent = async (req, res) => {
try {
const { noteId } = req.params;
const updated = await noteService.markReminderAsSent(noteId);
res.status(200).json({
success: true,
data: updated,
message: 'Reminder označený ako odoslaný',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};

View File

@@ -0,0 +1,316 @@
import * as projectService from '../services/project.service.js';
import * as noteService from '../services/note.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/**
* Get all projects
* GET /api/projects?search=query&companyId=xxx
*/
export const getAllProjects = async (req, res) => {
try {
const { search, companyId } = req.query;
const projects = await projectService.getAllProjects(search, companyId);
res.status(200).json({
success: true,
count: projects.length,
data: projects,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get project by ID
* GET /api/projects/:projectId
*/
export const getProjectById = async (req, res) => {
try {
const { projectId } = req.params;
const project = await projectService.getProjectById(projectId);
res.status(200).json({
success: true,
data: project,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get project with relations (company, todos, notes, timesheets)
* GET /api/projects/:projectId/details
*/
export const getProjectWithRelations = async (req, res) => {
try {
const { projectId } = req.params;
const project = await projectService.getProjectWithRelations(projectId);
res.status(200).json({
success: true,
data: project,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create new project
* POST /api/projects
* Body: { name, description, companyId, status, startDate, endDate }
*/
export const createProject = async (req, res) => {
try {
const userId = req.userId;
const data = req.body;
const project = await projectService.createProject(userId, data);
res.status(201).json({
success: true,
data: project,
message: 'Projekt bol vytvorený',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update project
* PATCH /api/projects/:projectId
* Body: { name, description, companyId, status, startDate, endDate }
*/
export const updateProject = async (req, res) => {
try {
const { projectId } = req.params;
const data = req.body;
const project = await projectService.updateProject(projectId, data);
res.status(200).json({
success: true,
data: project,
message: 'Projekt bol aktualizovaný',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete project
* DELETE /api/projects/:projectId
*/
export const deleteProject = async (req, res) => {
try {
const { projectId } = req.params;
const result = await projectService.deleteProject(projectId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get project notes
* GET /api/projects/:projectId/notes
*/
export const getProjectNotes = async (req, res) => {
try {
const { projectId } = req.params;
const notes = await noteService.getNotesByProjectId(projectId);
res.status(200).json({
success: true,
count: notes.length,
data: notes,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Add project note
* POST /api/projects/:projectId/notes
*/
export const addProjectNote = async (req, res) => {
try {
const userId = req.userId;
const { projectId } = req.params;
const { content, reminderAt } = req.body;
const note = await noteService.createNote(userId, {
content,
projectId,
reminderDate: reminderAt, // Map reminderAt to reminderDate
});
res.status(201).json({
success: true,
data: note,
message: 'Poznámka bola pridaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update project note
* PATCH /api/projects/:projectId/notes/:noteId
*/
export const updateProjectNote = async (req, res) => {
try {
const { noteId } = req.params;
const { content, reminderAt } = req.body;
const note = await noteService.updateNote(noteId, {
content,
reminderDate: reminderAt, // Map reminderAt to reminderDate
});
res.status(200).json({
success: true,
data: note,
message: 'Poznámka bola aktualizovaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete project note
* DELETE /api/projects/:projectId/notes/:noteId
*/
export const deleteProjectNote = async (req, res) => {
try {
const { noteId } = req.params;
const result = await noteService.deleteNote(noteId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get project users (team members)
* GET /api/projects/:projectId/users
*/
export const getProjectUsers = async (req, res) => {
try {
const { projectId } = req.params;
const users = await projectService.getProjectUsers(projectId);
res.status(200).json({
success: true,
count: users.length,
data: users,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Assign user to project
* POST /api/projects/:projectId/users
* Body: { userId, role }
*/
export const assignUserToProject = async (req, res) => {
try {
const currentUserId = req.userId;
const { projectId } = req.params;
const { userId, role } = req.body;
const assignment = await projectService.assignUserToProject(
projectId,
userId,
currentUserId,
role
);
res.status(201).json({
success: true,
data: assignment,
message: 'Používateľ bol priradený k projektu',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Remove user from project
* DELETE /api/projects/:projectId/users/:userId
*/
export const removeUserFromProject = async (req, res) => {
try {
const { projectId, userId } = req.params;
const result = await projectService.removeUserFromProject(projectId, userId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update user role on project
* PATCH /api/projects/:projectId/users/:userId
* Body: { role }
*/
export const updateUserRoleOnProject = async (req, res) => {
try {
const { projectId, userId } = req.params;
const { role } = req.body;
const updated = await projectService.updateUserRoleOnProject(projectId, userId, role);
res.status(200).json({
success: true,
data: updated,
message: 'Rola používateľa bola aktualizovaná',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};

View File

@@ -0,0 +1,191 @@
import * as todoService from '../services/todo.service.js';
import { formatErrorResponse } from '../utils/errors.js';
/**
* Get all todos
* GET /api/todos?search=query&projectId=xxx&companyId=xxx&assignedTo=xxx&status=xxx
*/
export const getAllTodos = async (req, res) => {
try {
const { search, projectId, companyId, assignedTo, status } = req.query;
const filters = {
searchTerm: search,
projectId,
companyId,
assignedTo,
status,
};
const todos = await todoService.getAllTodos(filters);
res.status(200).json({
success: true,
count: todos.length,
data: todos,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get my todos (assigned to current user)
* GET /api/todos/my?status=xxx
*/
export const getMyTodos = async (req, res) => {
try {
const userId = req.userId;
const { status } = req.query;
const filters = {
assignedTo: userId,
status,
};
const todos = await todoService.getAllTodos(filters);
res.status(200).json({
success: true,
count: todos.length,
data: todos,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get todo by ID
* GET /api/todos/:todoId
*/
export const getTodoById = async (req, res) => {
try {
const { todoId } = req.params;
const todo = await todoService.getTodoById(todoId);
res.status(200).json({
success: true,
data: todo,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Get todo with relations (project, company, assigned user, notes)
* GET /api/todos/:todoId/details
*/
export const getTodoWithRelations = async (req, res) => {
try {
const { todoId } = req.params;
const todo = await todoService.getTodoWithRelations(todoId);
res.status(200).json({
success: true,
data: todo,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Create new todo
* POST /api/todos
* Body: { title, description, projectId, companyId, assignedTo, status, priority, dueDate }
*/
export const createTodo = async (req, res) => {
try {
const userId = req.userId;
const data = req.body;
const todo = await todoService.createTodo(userId, data);
res.status(201).json({
success: true,
data: todo,
message: 'Todo bolo vytvorené',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Update todo
* PATCH /api/todos/:todoId
* Body: { title, description, projectId, companyId, assignedTo, status, priority, dueDate }
*/
export const updateTodo = async (req, res) => {
try {
const { todoId } = req.params;
const data = req.body;
const todo = await todoService.updateTodo(todoId, data);
res.status(200).json({
success: true,
data: todo,
message: 'Todo bolo aktualizované',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Delete todo
* DELETE /api/todos/:todoId
*/
export const deleteTodo = async (req, res) => {
try {
const { todoId } = req.params;
const result = await todoService.deleteTodo(todoId);
res.status(200).json({
success: true,
message: result.message,
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};
/**
* Toggle todo completion status
* PATCH /api/todos/:todoId/toggle
*/
export const toggleTodo = async (req, res) => {
try {
const { todoId } = req.params;
// Get current todo
const todo = await todoService.getTodoById(todoId);
// Toggle completed status
const updated = await todoService.updateTodo(todoId, {
status: todo.status === 'completed' ? 'pending' : 'completed',
});
res.status(200).json({
success: true,
data: updated,
message: 'Todo status aktualizovaný',
});
} catch (error) {
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
res.status(error.statusCode || 500).json(errorResponse);
}
};