feat: Add dueDate (date+time) to notes and update reminders to datetime
- Add dueDate timestamp field to notes schema - Update note validators to accept dueDate - Update note service to handle dueDate in CRUD operations - Fix company and project controllers to pass dueDate - Fix route validations to include dueDate field 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -208,11 +208,12 @@ export const addCompanyNote = async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
const { companyId } = req.params;
|
const { companyId } = req.params;
|
||||||
const { content } = req.body;
|
const { content, dueDate } = req.body;
|
||||||
|
|
||||||
const note = await noteService.createNote(userId, {
|
const note = await noteService.createNote(userId, {
|
||||||
content,
|
content,
|
||||||
companyId,
|
companyId,
|
||||||
|
dueDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
@@ -232,10 +233,11 @@ export const addCompanyNote = async (req, res, next) => {
|
|||||||
export const updateCompanyNote = async (req, res, next) => {
|
export const updateCompanyNote = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { noteId } = req.params;
|
const { noteId } = req.params;
|
||||||
const { content } = req.body;
|
const { content, dueDate } = req.body;
|
||||||
|
|
||||||
const note = await noteService.updateNote(noteId, {
|
const note = await noteService.updateNote(noteId, {
|
||||||
content,
|
content,
|
||||||
|
dueDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -165,12 +165,12 @@ export const addProjectNote = async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const { content, reminderAt } = req.body;
|
const { content, dueDate } = req.body;
|
||||||
|
|
||||||
const note = await noteService.createNote(userId, {
|
const note = await noteService.createNote(userId, {
|
||||||
content,
|
content,
|
||||||
projectId,
|
projectId,
|
||||||
reminderDate: reminderAt, // Map reminderAt to reminderDate
|
dueDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
@@ -190,11 +190,11 @@ export const addProjectNote = async (req, res, next) => {
|
|||||||
export const updateProjectNote = async (req, res, next) => {
|
export const updateProjectNote = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { noteId } = req.params;
|
const { noteId } = req.params;
|
||||||
const { content, reminderAt } = req.body;
|
const { content, dueDate } = req.body;
|
||||||
|
|
||||||
const note = await noteService.updateNote(noteId, {
|
const note = await noteService.updateNote(noteId, {
|
||||||
content,
|
content,
|
||||||
reminderDate: reminderAt, // Map reminderAt to reminderDate
|
dueDate,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export const todoUsers = pgTable('todo_users', {
|
|||||||
todoUserUnique: unique('todo_user_unique').on(table.todoId, table.userId),
|
todoUserUnique: unique('todo_user_unique').on(table.todoId, table.userId),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Notes table - poznámky (bez reminder funkcionalít)
|
// Notes table - poznámky s voliteľným dátumom a časom splnenia
|
||||||
export const notes = pgTable('notes', {
|
export const notes = pgTable('notes', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
title: text('title'),
|
title: text('title'),
|
||||||
@@ -219,6 +219,7 @@ export const notes = pgTable('notes', {
|
|||||||
projectId: uuid('project_id').references(() => projects.id, { onDelete: 'cascade' }), // alebo projektu
|
projectId: uuid('project_id').references(() => projects.id, { onDelete: 'cascade' }), // alebo projektu
|
||||||
todoId: uuid('todo_id').references(() => todos.id, { onDelete: 'cascade' }), // alebo todo
|
todoId: uuid('todo_id').references(() => todos.id, { onDelete: 'cascade' }), // alebo todo
|
||||||
contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }), // alebo kontaktu
|
contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }), // alebo kontaktu
|
||||||
|
dueDate: timestamp('due_date'), // voliteľný dátum a čas splnenia (24h formát)
|
||||||
createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }),
|
createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }),
|
||||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ router.post(
|
|||||||
validateParams(z.object({ companyId: z.string().uuid() })),
|
validateParams(z.object({ companyId: z.string().uuid() })),
|
||||||
validateBody(z.object({
|
validateBody(z.object({
|
||||||
content: z.string().min(1),
|
content: z.string().min(1),
|
||||||
|
dueDate: z.string().optional().or(z.literal('')),
|
||||||
})),
|
})),
|
||||||
companyController.addCompanyNote
|
companyController.addCompanyNote
|
||||||
);
|
);
|
||||||
@@ -96,6 +97,7 @@ router.patch(
|
|||||||
})),
|
})),
|
||||||
validateBody(z.object({
|
validateBody(z.object({
|
||||||
content: z.string().min(1).optional(),
|
content: z.string().min(1).optional(),
|
||||||
|
dueDate: z.string().optional().or(z.literal('').or(z.null())),
|
||||||
})),
|
})),
|
||||||
companyController.updateCompanyNote
|
companyController.updateCompanyNote
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ router.post(
|
|||||||
validateParams(z.object({ projectId: z.string().uuid() })),
|
validateParams(z.object({ projectId: z.string().uuid() })),
|
||||||
validateBody(z.object({
|
validateBody(z.object({
|
||||||
content: z.string().min(1),
|
content: z.string().min(1),
|
||||||
reminderAt: z.string().optional().or(z.literal('')),
|
dueDate: z.string().optional().or(z.literal('')),
|
||||||
})),
|
})),
|
||||||
projectController.addProjectNote
|
projectController.addProjectNote
|
||||||
);
|
);
|
||||||
@@ -80,7 +80,7 @@ router.patch(
|
|||||||
})),
|
})),
|
||||||
validateBody(z.object({
|
validateBody(z.object({
|
||||||
content: z.string().min(1).optional(),
|
content: z.string().min(1).optional(),
|
||||||
reminderAt: z.string().optional().or(z.literal('').or(z.null())),
|
dueDate: z.string().optional().or(z.literal('').or(z.null())),
|
||||||
})),
|
})),
|
||||||
projectController.updateProjectNote
|
projectController.updateProjectNote
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export const getAllNotes = async (filters = {}) => {
|
|||||||
projectId: notes.projectId,
|
projectId: notes.projectId,
|
||||||
todoId: notes.todoId,
|
todoId: notes.todoId,
|
||||||
contactId: notes.contactId,
|
contactId: notes.contactId,
|
||||||
|
dueDate: notes.dueDate,
|
||||||
createdBy: notes.createdBy,
|
createdBy: notes.createdBy,
|
||||||
createdAt: notes.createdAt,
|
createdAt: notes.createdAt,
|
||||||
updatedAt: notes.updatedAt,
|
updatedAt: notes.updatedAt,
|
||||||
@@ -86,7 +87,7 @@ export const getNoteById = async (noteId) => {
|
|||||||
* Create new note
|
* Create new note
|
||||||
*/
|
*/
|
||||||
export const createNote = async (userId, data) => {
|
export const createNote = async (userId, data) => {
|
||||||
const { title, content, companyId, projectId, todoId, contactId } = data;
|
const { title, content, companyId, projectId, todoId, contactId, dueDate } = data;
|
||||||
|
|
||||||
// Verify company exists if provided
|
// Verify company exists if provided
|
||||||
if (companyId) {
|
if (companyId) {
|
||||||
@@ -149,6 +150,7 @@ export const createNote = async (userId, data) => {
|
|||||||
projectId: projectId || null,
|
projectId: projectId || null,
|
||||||
todoId: todoId || null,
|
todoId: todoId || null,
|
||||||
contactId: contactId || null,
|
contactId: contactId || null,
|
||||||
|
dueDate: dueDate ? new Date(dueDate) : null,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
@@ -162,7 +164,7 @@ export const createNote = async (userId, data) => {
|
|||||||
export const updateNote = async (noteId, data) => {
|
export const updateNote = async (noteId, data) => {
|
||||||
const note = await getNoteById(noteId);
|
const note = await getNoteById(noteId);
|
||||||
|
|
||||||
const { title, content, companyId, projectId, todoId, contactId } = data;
|
const { title, content, companyId, projectId, todoId, contactId, dueDate } = data;
|
||||||
|
|
||||||
// Verify company exists if being changed
|
// Verify company exists if being changed
|
||||||
if (companyId !== undefined && companyId !== null && companyId !== note.companyId) {
|
if (companyId !== undefined && companyId !== null && companyId !== note.companyId) {
|
||||||
@@ -216,6 +218,12 @@ export const updateNote = async (noteId, data) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Spracovanie dueDate - konverzia na Date objekt alebo null
|
||||||
|
let parsedDueDate = note.dueDate;
|
||||||
|
if (dueDate !== undefined) {
|
||||||
|
parsedDueDate = dueDate ? new Date(dueDate) : null;
|
||||||
|
}
|
||||||
|
|
||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(notes)
|
.update(notes)
|
||||||
.set({
|
.set({
|
||||||
@@ -225,6 +233,7 @@ export const updateNote = async (noteId, data) => {
|
|||||||
projectId: projectId !== undefined ? projectId : note.projectId,
|
projectId: projectId !== undefined ? projectId : note.projectId,
|
||||||
todoId: todoId !== undefined ? todoId : note.todoId,
|
todoId: todoId !== undefined ? todoId : note.todoId,
|
||||||
contactId: contactId !== undefined ? contactId : note.contactId,
|
contactId: contactId !== undefined ? contactId : note.contactId,
|
||||||
|
dueDate: parsedDueDate,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
.where(eq(notes.id, noteId))
|
.where(eq(notes.id, noteId))
|
||||||
@@ -258,6 +267,7 @@ export const getNotesByCompanyId = async (companyId) => {
|
|||||||
projectId: notes.projectId,
|
projectId: notes.projectId,
|
||||||
todoId: notes.todoId,
|
todoId: notes.todoId,
|
||||||
contactId: notes.contactId,
|
contactId: notes.contactId,
|
||||||
|
dueDate: notes.dueDate,
|
||||||
createdBy: notes.createdBy,
|
createdBy: notes.createdBy,
|
||||||
createdAt: notes.createdAt,
|
createdAt: notes.createdAt,
|
||||||
updatedAt: notes.updatedAt,
|
updatedAt: notes.updatedAt,
|
||||||
@@ -286,6 +296,7 @@ export const getNotesByProjectId = async (projectId) => {
|
|||||||
projectId: notes.projectId,
|
projectId: notes.projectId,
|
||||||
todoId: notes.todoId,
|
todoId: notes.todoId,
|
||||||
contactId: notes.contactId,
|
contactId: notes.contactId,
|
||||||
|
dueDate: notes.dueDate,
|
||||||
createdBy: notes.createdBy,
|
createdBy: notes.createdBy,
|
||||||
createdAt: notes.createdAt,
|
createdAt: notes.createdAt,
|
||||||
updatedAt: notes.updatedAt,
|
updatedAt: notes.updatedAt,
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const updateTodoSchema = z.object({
|
|||||||
dueDate: z.string().optional().or(z.literal('').or(z.null())),
|
dueDate: z.string().optional().or(z.literal('').or(z.null())),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Note validators (without reminder functionality)
|
// Note validators (s voliteľným dueDate pre dátum a čas)
|
||||||
export const createNoteSchema = z.object({
|
export const createNoteSchema = z.object({
|
||||||
title: z.string().max(255).optional(),
|
title: z.string().max(255).optional(),
|
||||||
content: z
|
content: z
|
||||||
@@ -94,6 +94,7 @@ export const createNoteSchema = z.object({
|
|||||||
projectId: z.string().uuid('Neplatný formát project ID').optional().or(z.literal('')),
|
projectId: z.string().uuid('Neplatný formát project ID').optional().or(z.literal('')),
|
||||||
todoId: z.string().uuid('Neplatný formát todo ID').optional().or(z.literal('')),
|
todoId: z.string().uuid('Neplatný formát todo ID').optional().or(z.literal('')),
|
||||||
contactId: z.string().uuid('Neplatný formát contact ID').optional().or(z.literal('')),
|
contactId: z.string().uuid('Neplatný formát contact ID').optional().or(z.literal('')),
|
||||||
|
dueDate: z.string().optional().or(z.literal('')), // ISO string s dátumom a časom (24h formát)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateNoteSchema = z.object({
|
export const updateNoteSchema = z.object({
|
||||||
@@ -103,6 +104,7 @@ export const updateNoteSchema = z.object({
|
|||||||
projectId: z.string().uuid('Neplatný formát project ID').optional().or(z.literal('').or(z.null())),
|
projectId: z.string().uuid('Neplatný formát project ID').optional().or(z.literal('').or(z.null())),
|
||||||
todoId: z.string().uuid('Neplatný formát todo ID').optional().or(z.literal('').or(z.null())),
|
todoId: z.string().uuid('Neplatný formát todo ID').optional().or(z.literal('').or(z.null())),
|
||||||
contactId: z.string().uuid('Neplatný formát contact ID').optional().or(z.literal('').or(z.null())),
|
contactId: z.string().uuid('Neplatný formát contact ID').optional().or(z.literal('').or(z.null())),
|
||||||
|
dueDate: z.string().optional().or(z.literal('').or(z.null())), // ISO string s dátumom a časom (24h formát)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Company reminder validators (with dueDate)
|
// Company reminder validators (with dueDate)
|
||||||
|
|||||||
Reference in New Issue
Block a user