feat(projects): editable notes with edit stamp + NoteCard bubble component
- author-only note editing (PUT /projects/:id/notes/:noteId, strict schema, audit with old/new content); edits stamped via new edited_at column (migration 20260612160000) and shown as '· upraveno <datum>' - note deletion tightened to admin-only server-side (was any projects.edit holder via direct API) - new NoteCard component: chat-bubble note row (initials avatar, wrapping header, inline editor, actions in the bubble corner so they don't squeeze content on mobile), table-matching edit/delete icons - Odin project detail carries the edited_at stamp; route-level tests cover author-edit, foreign-edit 403, validation, admin-only delete Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -283,3 +283,37 @@ export async function deleteProjectNote(projectId: number, noteId: number) {
|
||||
await prisma.project_notes.delete({ where: { id: noteId } });
|
||||
return note;
|
||||
}
|
||||
|
||||
/**
|
||||
* Author-only note edit: a note belongs to whoever wrote it (even admins
|
||||
* edit only their own — editing someone else's words would falsify
|
||||
* authorship; admins delete instead). Stamps edited_at for the UI marker.
|
||||
*/
|
||||
export async function updateProjectNote(
|
||||
projectId: number,
|
||||
noteId: number,
|
||||
userId: number,
|
||||
content: string,
|
||||
): Promise<
|
||||
| null
|
||||
| { error: string; status: number; note?: never }
|
||||
| {
|
||||
error?: never;
|
||||
status?: never;
|
||||
note: Awaited<ReturnType<typeof prisma.project_notes.update>>;
|
||||
previousContent: string | null;
|
||||
}
|
||||
> {
|
||||
const note = await prisma.project_notes.findFirst({
|
||||
where: { id: noteId, project_id: projectId },
|
||||
});
|
||||
if (!note) return null;
|
||||
if (note.user_id !== userId) {
|
||||
return { error: "Upravit lze pouze vlastní poznámku", status: 403 };
|
||||
}
|
||||
const updated = await prisma.project_notes.update({
|
||||
where: { id: noteId },
|
||||
data: { content, edited_at: new Date() },
|
||||
});
|
||||
return { note: updated, previousContent: note.content };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user