Add dueDate to reminders, remove reminder from notes
Schema changes: - Added dueDate field to companyReminders table - Removed reminderDate and reminderSent from notes table Backend changes: - Updated company-reminder.service with dueDate handling - Added getUpcomingReminders function for dashboard - Simplified note.service (removed reminder logic) - Updated validators and routes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -203,12 +203,11 @@ export const addCompanyNote = async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
const { companyId } = req.params;
|
const { companyId } = req.params;
|
||||||
const { content, reminderAt } = req.body;
|
const { content } = req.body;
|
||||||
|
|
||||||
const note = await noteService.createNote(userId, {
|
const note = await noteService.createNote(userId, {
|
||||||
content,
|
content,
|
||||||
companyId,
|
companyId,
|
||||||
reminderDate: reminderAt, // Map reminderAt to reminderDate
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
@@ -229,11 +228,10 @@ export const addCompanyNote = async (req, res) => {
|
|||||||
export const updateCompanyNote = async (req, res) => {
|
export const updateCompanyNote = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { noteId } = req.params;
|
const { noteId } = req.params;
|
||||||
const { content, reminderAt } = req.body;
|
const { content } = req.body;
|
||||||
|
|
||||||
const note = await noteService.updateNote(noteId, {
|
const note = await noteService.updateNote(noteId, {
|
||||||
content,
|
content,
|
||||||
reminderDate: reminderAt, // Map reminderAt to reminderDate
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
@@ -291,9 +289,9 @@ export const getCompanyReminders = async (req, res) => {
|
|||||||
export const createCompanyReminder = async (req, res) => {
|
export const createCompanyReminder = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { companyId } = req.params;
|
const { companyId } = req.params;
|
||||||
const { description, isChecked } = req.body;
|
const { description, dueDate, isChecked } = req.body;
|
||||||
|
|
||||||
const reminder = await companyReminderService.createReminder(companyId, { description, isChecked });
|
const reminder = await companyReminderService.createReminder(companyId, { description, dueDate, isChecked });
|
||||||
|
|
||||||
res.status(201).json({
|
res.status(201).json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -309,9 +307,9 @@ export const createCompanyReminder = async (req, res) => {
|
|||||||
export const updateCompanyReminder = async (req, res) => {
|
export const updateCompanyReminder = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { companyId, reminderId } = req.params;
|
const { companyId, reminderId } = req.params;
|
||||||
const { description, isChecked } = req.body;
|
const { description, dueDate, isChecked } = req.body;
|
||||||
|
|
||||||
const reminder = await companyReminderService.updateReminder(companyId, reminderId, { description, isChecked });
|
const reminder = await companyReminderService.updateReminder(companyId, reminderId, { description, dueDate, isChecked });
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -365,3 +363,17 @@ export const getReminderCountsByCompany = async (_req, res) => {
|
|||||||
res.status(error.statusCode || 500).json(errorResponse);
|
res.status(error.statusCode || 500).json(errorResponse);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUpcomingReminders = async (_req, res) => {
|
||||||
|
try {
|
||||||
|
const reminders = await companyReminderService.getUpcomingReminders();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -132,11 +132,12 @@ export const projects = pgTable('projects', {
|
|||||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Company reminders table - jednoduché pripomienky naviazané na firmu
|
// Company reminders table - pripomienky naviazané na firmu s dátumom
|
||||||
export const companyReminders = pgTable('company_remind', {
|
export const companyReminders = pgTable('company_remind', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }).notNull(),
|
companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }).notNull(),
|
||||||
description: text('description').notNull(),
|
description: text('description').notNull(),
|
||||||
|
dueDate: timestamp('due_date'), // kedy má byť splnená
|
||||||
isChecked: boolean('is_checked').default(false).notNull(),
|
isChecked: boolean('is_checked').default(false).notNull(),
|
||||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||||
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
updatedAt: timestamp('updated_at').defaultNow().notNull(),
|
||||||
@@ -181,7 +182,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
|
// Notes table - poznámky (bez reminder funkcionalít)
|
||||||
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'),
|
||||||
@@ -190,8 +191,6 @@ 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
|
||||||
reminderDate: timestamp('reminder_date'), // dátum a čas pre reminder
|
|
||||||
reminderSent: boolean('reminder_sent').default(false).notNull(), // či už bol reminder odoslaný
|
|
||||||
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(),
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ router.use(authenticate);
|
|||||||
// Reminder summaries (must be before :companyId routes)
|
// Reminder summaries (must be before :companyId routes)
|
||||||
router.get('/reminders/summary', companyController.getReminderSummary);
|
router.get('/reminders/summary', companyController.getReminderSummary);
|
||||||
router.get('/reminders/counts', companyController.getReminderCountsByCompany);
|
router.get('/reminders/counts', companyController.getReminderCountsByCompany);
|
||||||
|
router.get('/reminders/upcoming', companyController.getUpcomingReminders);
|
||||||
|
|
||||||
// Company unread email summary
|
// Company unread email summary
|
||||||
router.get('/email-unread', companyController.getCompanyUnreadCounts);
|
router.get('/email-unread', companyController.getCompanyUnreadCounts);
|
||||||
@@ -72,7 +73,6 @@ 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),
|
||||||
reminderAt: z.string().optional().or(z.literal('')),
|
|
||||||
})),
|
})),
|
||||||
companyController.addCompanyNote
|
companyController.addCompanyNote
|
||||||
);
|
);
|
||||||
@@ -85,7 +85,6 @@ 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())),
|
|
||||||
})),
|
})),
|
||||||
companyController.updateCompanyNote
|
companyController.updateCompanyNote
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { db } from '../config/database.js';
|
import { db } from '../config/database.js';
|
||||||
import { companies, companyReminders } from '../db/schema.js';
|
import { companies, companyReminders } from '../db/schema.js';
|
||||||
import { eq, desc, sql } from 'drizzle-orm';
|
import { eq, desc, sql, and, lte, gte, isNull, or } from 'drizzle-orm';
|
||||||
import { NotFoundError, BadRequestError } from '../utils/errors.js';
|
import { NotFoundError, BadRequestError } from '../utils/errors.js';
|
||||||
|
|
||||||
const ensureCompanyExists = async (companyId) => {
|
const ensureCompanyExists = async (companyId) => {
|
||||||
const [company] = await db
|
const [company] = await db
|
||||||
.select({ id: companies.id })
|
.select({ id: companies.id, name: companies.name })
|
||||||
.from(companies)
|
.from(companies)
|
||||||
.where(eq(companies.id, companyId))
|
.where(eq(companies.id, companyId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
@@ -13,6 +13,8 @@ const ensureCompanyExists = async (companyId) => {
|
|||||||
if (!company) {
|
if (!company) {
|
||||||
throw new NotFoundError('Firma nenájdená');
|
throw new NotFoundError('Firma nenájdená');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return company;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getReminderById = async (reminderId) => {
|
const getReminderById = async (reminderId) => {
|
||||||
@@ -36,7 +38,7 @@ export const getRemindersByCompanyId = async (companyId) => {
|
|||||||
.select()
|
.select()
|
||||||
.from(companyReminders)
|
.from(companyReminders)
|
||||||
.where(eq(companyReminders.companyId, companyId))
|
.where(eq(companyReminders.companyId, companyId))
|
||||||
.orderBy(desc(companyReminders.createdAt));
|
.orderBy(companyReminders.dueDate, desc(companyReminders.createdAt));
|
||||||
|
|
||||||
return reminders;
|
return reminders;
|
||||||
};
|
};
|
||||||
@@ -54,6 +56,7 @@ export const createReminder = async (companyId, data) => {
|
|||||||
.values({
|
.values({
|
||||||
companyId,
|
companyId,
|
||||||
description,
|
description,
|
||||||
|
dueDate: data.dueDate ? new Date(data.dueDate) : null,
|
||||||
isChecked: data.isChecked ?? false,
|
isChecked: data.isChecked ?? false,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
@@ -80,6 +83,7 @@ export const updateReminder = async (companyId, reminderId, data) => {
|
|||||||
.update(companyReminders)
|
.update(companyReminders)
|
||||||
.set({
|
.set({
|
||||||
description: trimmedDescription,
|
description: trimmedDescription,
|
||||||
|
dueDate: data.dueDate !== undefined ? (data.dueDate ? new Date(data.dueDate) : null) : reminder.dueDate,
|
||||||
isChecked: data.isChecked !== undefined ? data.isChecked : reminder.isChecked,
|
isChecked: data.isChecked !== undefined ? data.isChecked : reminder.isChecked,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
@@ -131,3 +135,37 @@ export const getReminderCountsByCompany = async () => {
|
|||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get upcoming reminders for dashboard
|
||||||
|
* Returns reminders due within the next 5 days that are not checked
|
||||||
|
* Includes company name for display
|
||||||
|
*/
|
||||||
|
export const getUpcomingReminders = async () => {
|
||||||
|
const now = new Date();
|
||||||
|
const fiveDaysFromNow = new Date();
|
||||||
|
fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5);
|
||||||
|
|
||||||
|
const reminders = await db
|
||||||
|
.select({
|
||||||
|
id: companyReminders.id,
|
||||||
|
description: companyReminders.description,
|
||||||
|
dueDate: companyReminders.dueDate,
|
||||||
|
isChecked: companyReminders.isChecked,
|
||||||
|
companyId: companyReminders.companyId,
|
||||||
|
companyName: companies.name,
|
||||||
|
createdAt: companyReminders.createdAt,
|
||||||
|
})
|
||||||
|
.from(companyReminders)
|
||||||
|
.leftJoin(companies, eq(companyReminders.companyId, companies.id))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(companyReminders.isChecked, false),
|
||||||
|
lte(companyReminders.dueDate, fiveDaysFromNow),
|
||||||
|
gte(companyReminders.dueDate, now)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.orderBy(companyReminders.dueDate);
|
||||||
|
|
||||||
|
return reminders;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,21 +1,8 @@
|
|||||||
import { db } from '../config/database.js';
|
import { db } from '../config/database.js';
|
||||||
import { notes, companies, projects, todos, contacts } from '../db/schema.js';
|
import { notes, companies, projects, todos, contacts } from '../db/schema.js';
|
||||||
import { eq, desc, ilike, or, and, lte, isNull, not } from 'drizzle-orm';
|
import { eq, desc, ilike, or, and } from 'drizzle-orm';
|
||||||
import { NotFoundError } from '../utils/errors.js';
|
import { NotFoundError } from '../utils/errors.js';
|
||||||
|
|
||||||
/**
|
|
||||||
* Map note fields for frontend compatibility
|
|
||||||
* reminderDate → reminderAt
|
|
||||||
*/
|
|
||||||
const mapNoteForFrontend = (note) => {
|
|
||||||
if (!note) return note;
|
|
||||||
const { reminderDate, ...rest } = note;
|
|
||||||
return {
|
|
||||||
...rest,
|
|
||||||
reminderAt: reminderDate,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all notes
|
* Get all notes
|
||||||
* Optionally filter by search, company, project, todo, or contact
|
* Optionally filter by search, company, project, todo, or contact
|
||||||
@@ -56,8 +43,7 @@ export const getAllNotes = async (filters = {}) => {
|
|||||||
query = query.where(and(...conditions));
|
query = query.where(and(...conditions));
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await query.orderBy(desc(notes.createdAt));
|
return await query.orderBy(desc(notes.createdAt));
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,14 +60,14 @@ export const getNoteById = async (noteId) => {
|
|||||||
throw new NotFoundError('Poznámka nenájdená');
|
throw new NotFoundError('Poznámka nenájdená');
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapNoteForFrontend(note);
|
return note;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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, reminderDate } = data;
|
const { title, content, companyId, projectId, todoId, contactId } = data;
|
||||||
|
|
||||||
// Verify company exists if provided
|
// Verify company exists if provided
|
||||||
if (companyId) {
|
if (companyId) {
|
||||||
@@ -144,13 +130,11 @@ export const createNote = async (userId, data) => {
|
|||||||
projectId: projectId || null,
|
projectId: projectId || null,
|
||||||
todoId: todoId || null,
|
todoId: todoId || null,
|
||||||
contactId: contactId || null,
|
contactId: contactId || null,
|
||||||
reminderDate: reminderDate ? new Date(reminderDate) : null,
|
|
||||||
reminderSent: false,
|
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return mapNoteForFrontend(newNote);
|
return newNote;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +143,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, reminderDate } = data;
|
const { title, content, companyId, projectId, todoId, contactId } = 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) {
|
||||||
@@ -222,14 +206,12 @@ 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,
|
||||||
reminderDate: reminderDate !== undefined ? (reminderDate ? new Date(reminderDate) : null) : note.reminderDate,
|
|
||||||
reminderSent: reminderDate !== undefined ? false : note.reminderSent, // Reset reminderSent if reminderDate changes
|
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
.where(eq(notes.id, noteId))
|
.where(eq(notes.id, noteId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
return mapNoteForFrontend(updated);
|
return updated;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -247,103 +229,42 @@ export const deleteNote = async (noteId) => {
|
|||||||
* Get notes by company ID
|
* Get notes by company ID
|
||||||
*/
|
*/
|
||||||
export const getNotesByCompanyId = async (companyId) => {
|
export const getNotesByCompanyId = async (companyId) => {
|
||||||
const result = await db
|
return await db
|
||||||
.select()
|
.select()
|
||||||
.from(notes)
|
.from(notes)
|
||||||
.where(eq(notes.companyId, companyId))
|
.where(eq(notes.companyId, companyId))
|
||||||
.orderBy(desc(notes.createdAt));
|
.orderBy(desc(notes.createdAt));
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get notes by project ID
|
* Get notes by project ID
|
||||||
*/
|
*/
|
||||||
export const getNotesByProjectId = async (projectId) => {
|
export const getNotesByProjectId = async (projectId) => {
|
||||||
const result = await db
|
return await db
|
||||||
.select()
|
.select()
|
||||||
.from(notes)
|
.from(notes)
|
||||||
.where(eq(notes.projectId, projectId))
|
.where(eq(notes.projectId, projectId))
|
||||||
.orderBy(desc(notes.createdAt));
|
.orderBy(desc(notes.createdAt));
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get notes by todo ID
|
* Get notes by todo ID
|
||||||
*/
|
*/
|
||||||
export const getNotesByTodoId = async (todoId) => {
|
export const getNotesByTodoId = async (todoId) => {
|
||||||
const result = await db
|
return await db
|
||||||
.select()
|
.select()
|
||||||
.from(notes)
|
.from(notes)
|
||||||
.where(eq(notes.todoId, todoId))
|
.where(eq(notes.todoId, todoId))
|
||||||
.orderBy(desc(notes.createdAt));
|
.orderBy(desc(notes.createdAt));
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get notes by contact ID
|
* Get notes by contact ID
|
||||||
*/
|
*/
|
||||||
export const getNotesByContactId = async (contactId) => {
|
export const getNotesByContactId = async (contactId) => {
|
||||||
const result = await db
|
return await db
|
||||||
.select()
|
.select()
|
||||||
.from(notes)
|
.from(notes)
|
||||||
.where(eq(notes.contactId, contactId))
|
.where(eq(notes.contactId, contactId))
|
||||||
.orderBy(desc(notes.createdAt));
|
.orderBy(desc(notes.createdAt));
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get pending reminders (reminders that are due and not sent)
|
|
||||||
*/
|
|
||||||
export const getPendingReminders = async () => {
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
const result = await db
|
|
||||||
.select()
|
|
||||||
.from(notes)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
not(isNull(notes.reminderDate)),
|
|
||||||
lte(notes.reminderDate, now),
|
|
||||||
eq(notes.reminderSent, false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.orderBy(notes.reminderDate);
|
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark reminder as sent
|
|
||||||
*/
|
|
||||||
export const markReminderAsSent = async (noteId) => {
|
|
||||||
const [updated] = await db
|
|
||||||
.update(notes)
|
|
||||||
.set({
|
|
||||||
reminderSent: true,
|
|
||||||
updatedAt: new Date(),
|
|
||||||
})
|
|
||||||
.where(eq(notes.id, noteId))
|
|
||||||
.returning();
|
|
||||||
|
|
||||||
return mapNoteForFrontend(updated);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get upcoming reminders for a user (created by user, not sent yet)
|
|
||||||
*/
|
|
||||||
export const getUpcomingRemindersForUser = async (userId) => {
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
const result = await db
|
|
||||||
.select()
|
|
||||||
.from(notes)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(notes.createdBy, userId),
|
|
||||||
not(isNull(notes.reminderDate)),
|
|
||||||
lte(notes.reminderDate, now),
|
|
||||||
eq(notes.reminderSent, false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.orderBy(notes.reminderDate);
|
|
||||||
return result.map(mapNoteForFrontend);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
// Note validators (without reminder functionality)
|
||||||
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,7 +94,6 @@ 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('')),
|
||||||
reminderDate: z.string().optional().or(z.literal('')),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateNoteSchema = z.object({
|
export const updateNoteSchema = z.object({
|
||||||
@@ -104,21 +103,22 @@ 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())),
|
||||||
reminderDate: z.string().optional().or(z.literal('').or(z.null())),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Company reminder validators
|
// Company reminder validators (with dueDate)
|
||||||
export const createCompanyReminderSchema = z.object({
|
export const createCompanyReminderSchema = z.object({
|
||||||
description: z.string().min(1).max(1000),
|
description: z.string().min(1).max(1000),
|
||||||
|
dueDate: z.string().optional().or(z.literal('')),
|
||||||
isChecked: z.boolean().optional(),
|
isChecked: z.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateCompanyReminderSchema = z.object({
|
export const updateCompanyReminderSchema = z.object({
|
||||||
description: z.string().min(1).max(1000).optional(),
|
description: z.string().min(1).max(1000).optional(),
|
||||||
|
dueDate: z.string().optional().or(z.literal('').or(z.null())),
|
||||||
isChecked: z.boolean().optional(),
|
isChecked: z.boolean().optional(),
|
||||||
}).refine(
|
}).refine(
|
||||||
(data) => data.description !== undefined || data.isChecked !== undefined,
|
(data) => data.description !== undefined || data.isChecked !== undefined || data.dueDate !== undefined,
|
||||||
{ message: 'Je potrebné zadať description alebo isChecked' }
|
{ message: 'Je potrebné zadať description, dueDate alebo isChecked' }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Time Tracking validators
|
// Time Tracking validators
|
||||||
|
|||||||
Reference in New Issue
Block a user