Feat: Implement CRUD for project links and notes with UX improvements - #11
Conversation
… associated forms and API hooks
…-outside dismissal and tab-change resetting
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughO PR adiciona suporte a notas e links no workspace, com APIs, modelos, queries, mutations, formulários modais e gerenciamento de tags. Também amplia as associações de tags e corrige o prefixo das rotas REST de notas e seus testes. ChangesConteúdo do workspace
Rota REST de notas
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ProjectDetailView
participant NoteForm
participant useCreateNoteMutation
participant notesApi
User->>ProjectDetailView: abre o formulário de nota
ProjectDetailView->>NoteForm: renderiza modal de criação
User->>NoteForm: envia título e conteúdo
NoteForm->>useCreateNoteMutation: submete dados
useCreateNoteMutation->>notesApi: cria nota
notesApi-->>useCreateNoteMutation: retorna sucesso
useCreateNoteMutation-->>ProjectDetailView: invalida a lista de notas
ProjectDetailView-->>User: exibe a nota atualizada
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (5)
frontend/src/features/tags/hooks/useTags.ts (2)
74-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMapeamento
itemType → itemKeyduplicado entreuseAssociateTagMutationeuseDisassociateTagMutation.A mesma lógica if/else-if de mapeamento de
typeLowerpara a chave de invalidação (snippets/problems/notes/links) está repetida nas duas mutations. Centralizar em uma função utilitária evita divergência futura (como a que já ocorreu emuseUpdateTagMutation, cuja lista de invalidação ficou incompleta por não reaproveitar esse mapeamento).
frontend/src/features/tags/hooks/useTags.ts#L74-L78: extrair a lógica deuseAssociateTagMutationpara uma funçãogetItemKeyForType(itemType: string): stringcompartilhada.frontend/src/features/tags/hooks/useTags.ts#L90-L94: reutilizar a mesma funçãogetItemKeyForTypeemuseDisassociateTagMutationem vez de duplicar o if/else-if.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/features/tags/hooks/useTags.ts` around lines 74 - 78, Centralize the duplicated itemType-to-itemKey mapping in a shared getItemKeyForType(itemType: string): string function. Update useAssociateTagMutation at frontend/src/features/tags/hooks/useTags.ts:74-78 to use the helper, and update useDisassociateTagMutation at frontend/src/features/tags/hooks/useTags.ts:90-94 to reuse it instead of maintaining a separate if/else-if chain.
18-25: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
useSearchTagsQuerysem debounce pode gerar excesso de requisições.A query é habilitada a cada mudança de
namenão vazio, sem debounce. Se usada em um campo de busca "ao digitar", isso disparará um GET por tecla.Verifique como esse hook é consumido (ex.: popover de tags em
ProjectDetailView.tsx) para confirmar se já existe debounce no lado do chamador antes de decidir se o ajuste deve entrar aqui ou lá.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/features/tags/hooks/useTags.ts` around lines 18 - 25, Verifique os consumidores de useSearchTagsQuery, especialmente o popover de tags em ProjectDetailView.tsx, para confirmar se name já é debounceado antes da consulta. Se não houver debounce no chamador, adicione debounce ao fluxo de busca preservando o enabled para nomes não vazios e use o valor debounced em tagsKeys.search e tagsApi.search; se já existir, mantenha o hook sem duplicar o atraso.frontend/src/features/notes/hooks/useNotes.ts (1)
40-43: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winInvalidação redundante antes de
setQueryDatana mutation de update.
invalidateQueriesparanotesKeys.detaildispara um refetch imediato (comportamento padrão do React Query v5 para queries ativas) para a mesma chave que é sobrescrita na linha seguinte comsetQueryData. Isso gera uma requisição de rede desnecessária sempre que a nota editada estiver montada.♻️ Ajuste sugerido
onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: notesKeys.all(projectId) }); - queryClient.invalidateQueries({ queryKey: notesKeys.detail(projectId, noteId) }); queryClient.setQueryData(notesKeys.detail(projectId, noteId), data); },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/features/notes/hooks/useNotes.ts` around lines 40 - 43, Remova a invalidação de notesKeys.detail dentro do callback onSuccess da mutation de atualização, mantendo a invalidação de notesKeys.all e a atualização direta via setQueryData para a nota editada.frontend/src/components/TagsManagerModal.tsx (1)
45-75: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLógica de foco duplicada em relação ao
ConfirmModal.Os dois
useEffects de restauração de foco e Tab-trap replicam exatamente o padrão já implementado emConfirmModal.tsx. Extrair um hook compartilhado (ex.:useFocusTrap(modalRef, isOpen, initialFocusRef)) reduziria a manutenção duplicada, especialmente se outros modais (NoteForm/LinkForm) repetirem o mesmo padrão.Also applies to: 77-110
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/TagsManagerModal.tsx` around lines 45 - 75, Extraia a lógica compartilhada de captura, foco inicial, restauração de foco e Tab-trap presente em TagsManagerModal e ConfirmModal para um hook reutilizável, como useFocusTrap. Atualize ambos os componentes para usar esse hook com modalRef, isOpen e a ref do foco inicial, removendo os useEffect e refs duplicados sem alterar o comportamento existente.frontend/src/features/links/hooks/useLinks.ts (1)
11-16: 🚀 Performance & Scalability | 🔵 TrivialPaginação fixa em
size = 100.A busca/filtragem em
ProjectDetailView(filteredLinks/filteredNotes) é feita no cliente sobrecontent, então links (e notas, que seguem o mesmo padrão) além dos primeiros 100 ficam invisíveis e não pesquisáveis. Para projetos com muitos itens, considere paginação incremental (ex.:useInfiniteQuery) ou busca server-side.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/features/links/hooks/useLinks.ts` around lines 11 - 16, Update useLinksQuery and the ProjectDetailView filteredLinks/filteredNotes flow so client-side search can access all project links and notes instead of only the first 100; implement incremental pagination (such as useInfiniteQuery) and aggregate all fetched pages before filtering, or reuse an existing server-side search mechanism if available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@backend/src/main/java/com/devaulty/backend/adapter/in/web/note/NoteController.java`:
- Line 24: Atualize o endpoint definido em NoteController e todos os
consumidores relacionados, incluindo NoteControllerIT, para usar o prefixo
correto /api/v1/projects/{projectId}/notes em vez de /ap1/v1. Preserve o
restante do caminho e dos comportamentos existentes.
In `@frontend/src/components/TagsManagerModal.tsx`:
- Around line 280-289: Adicione um nome acessível a cada botão de cor no
mapeamento PRESET_COLORS do formulário de edição inline, usando title e/ou
aria-label com o valor da cor, mantendo o comportamento existente de
setEditingTagColor.
- Around line 355-365: Update the warningText in the Delete Confirmation Modal
to mention that deleting the tag will disassociate it from snippets, problems,
notes, and links across the project. Leave the confirmDeleteTag flow and other
modal properties unchanged.
In `@frontend/src/features/notes/components/NoteForm.tsx`:
- Around line 63-93: Update the keydown handlers in NoteForm.tsx (lines 63-93)
and LinkForm.tsx (lines 65-95) to call onClose() when e.key is "Escape", while
preserving the existing Tab focus-trap behavior.
- Around line 276-293: Remove the unused Suspense wrappers and their loading
fallbacks around EditNoteFormModal in NoteForm.tsx (lines 276-293) and
EditLinkFormModal in LinkForm.tsx (lines 300-316), since both components handle
useQuery loading states internally; leave the modal rendering and props
unchanged.
In `@frontend/src/features/tags/hooks/useTags.ts`:
- Around line 41-46: Atualize o callback onSuccess de useUpdateTagMutation para
também invalidar as queries de notas e links do projeto, usando as chaves de
cache já empregadas por useAssociateTagMutation e useDisassociateTagMutation.
Preserve as invalidações existentes de tags, problems e snippets.
---
Nitpick comments:
In `@frontend/src/components/TagsManagerModal.tsx`:
- Around line 45-75: Extraia a lógica compartilhada de captura, foco inicial,
restauração de foco e Tab-trap presente em TagsManagerModal e ConfirmModal para
um hook reutilizável, como useFocusTrap. Atualize ambos os componentes para usar
esse hook com modalRef, isOpen e a ref do foco inicial, removendo os useEffect e
refs duplicados sem alterar o comportamento existente.
In `@frontend/src/features/links/hooks/useLinks.ts`:
- Around line 11-16: Update useLinksQuery and the ProjectDetailView
filteredLinks/filteredNotes flow so client-side search can access all project
links and notes instead of only the first 100; implement incremental pagination
(such as useInfiniteQuery) and aggregate all fetched pages before filtering, or
reuse an existing server-side search mechanism if available.
In `@frontend/src/features/notes/hooks/useNotes.ts`:
- Around line 40-43: Remova a invalidação de notesKeys.detail dentro do callback
onSuccess da mutation de atualização, mantendo a invalidação de notesKeys.all e
a atualização direta via setQueryData para a nota editada.
In `@frontend/src/features/tags/hooks/useTags.ts`:
- Around line 74-78: Centralize the duplicated itemType-to-itemKey mapping in a
shared getItemKeyForType(itemType: string): string function. Update
useAssociateTagMutation at frontend/src/features/tags/hooks/useTags.ts:74-78 to
use the helper, and update useDisassociateTagMutation at
frontend/src/features/tags/hooks/useTags.ts:90-94 to reuse it instead of
maintaining a separate if/else-if chain.
- Around line 18-25: Verifique os consumidores de useSearchTagsQuery,
especialmente o popover de tags em ProjectDetailView.tsx, para confirmar se name
já é debounceado antes da consulta. Se não houver debounce no chamador, adicione
debounce ao fluxo de busca preservando o enabled para nomes não vazios e use o
valor debounced em tagsKeys.search e tagsApi.search; se já existir, mantenha o
hook sem duplicar o atraso.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: ede4b7dc-dafc-4bbd-abbb-9b625f65c4eb
📒 Files selected for processing (15)
backend/src/main/java/com/devaulty/backend/adapter/in/web/note/NoteController.javafrontend/src/components/ProjectDetailView.tsxfrontend/src/components/TagsManagerModal.module.cssfrontend/src/components/TagsManagerModal.tsxfrontend/src/features/links/api/linksApi.tsfrontend/src/features/links/components/LinkForm.module.cssfrontend/src/features/links/components/LinkForm.tsxfrontend/src/features/links/hooks/useLinks.tsfrontend/src/features/notes/api/notesApi.tsfrontend/src/features/notes/components/NoteForm.module.cssfrontend/src/features/notes/components/NoteForm.tsxfrontend/src/features/notes/hooks/useNotes.tsfrontend/src/features/tags/api/tagsApi.tsfrontend/src/features/tags/hooks/useTags.tsfrontend/src/types/api.ts
…endpoint typos in integration tests, and update tag invalidation logic
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/features/notes/components/NoteForm.tsx (1)
63-97: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winInclua
onClosenas dependências do efeito. O handler captura a versão inicial do callback; como o pai passaonCloseinline, um rerender com o modal aberto pode deixar o Escape chamando uma referência obsoleta.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/features/notes/components/NoteForm.tsx` around lines 63 - 97, Atualize as dependências do useEffect que registra o handler de teclado em NoteForm para incluir onClose, garantindo que o Escape use a referência atual do callback quando o componente pai rerenderizar.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/features/notes/components/NoteForm.tsx`:
- Around line 65-68: Prevent Escape and overlay-click handlers from calling
onClose while isSubmitting in NoteForm.tsx and LinkForm.tsx. Update the relevant
useEffect handlers in both components to include isSubmitting in their
dependency arrays, while preserving closing behavior when submission is not
pending.
---
Outside diff comments:
In `@frontend/src/features/notes/components/NoteForm.tsx`:
- Around line 63-97: Atualize as dependências do useEffect que registra o
handler de teclado em NoteForm para incluir onClose, garantindo que o Escape use
a referência atual do callback quando o componente pai rerenderizar.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 103eb30c-a559-4bf1-bae6-c6be430acef0
📒 Files selected for processing (6)
backend/src/test/java/com/devaulty/backend/adapter/in/web/note/NoteControllerIT.javafrontend/src/components/TagsManagerModal.module.cssfrontend/src/components/TagsManagerModal.tsxfrontend/src/features/links/components/LinkForm.tsxfrontend/src/features/notes/components/NoteForm.tsxfrontend/src/features/tags/hooks/useTags.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/src/features/tags/hooks/useTags.ts
- frontend/src/components/TagsManagerModal.tsx
- frontend/src/components/TagsManagerModal.module.css
submission
Prevent NoteForm and LinkForm from closing when the user presses the
Escape key or clicks the overlay background while form submission is in
progress. Updated the keydown useEffect hook dependencies and overlay
onClick handlers to check and respect the isSubmitting state.
This pull request introduces a new tags management modal for the frontend and adds API and UI support for managing project links. It also fixes a typo in a backend controller route. The main changes are grouped below.
Tags Management Modal (Frontend)
TagsManagerModalReact component with full CRUD functionality for tags, including color selection, inline editing, deletion with confirmation, and keyboard accessibility features. (frontend/src/components/TagsManagerModal.tsx)frontend/src/components/TagsManagerModal.module.css)Project Links API and UI Support
linksApimodule to handle all CRUD operations for project links via the backend API. (frontend/src/features/links/api/linksApi.ts)frontend/src/features/links/components/LinkForm.module.css)Backend Route Fix
NoteControllerbackend route, changing/ap1/v1/...to/api/v1/...for correct API endpoint addressing. (backend/src/main/java/com/devaulty/backend/adapter/in/web/note/NoteController.java)Summary by CodeRabbit
Novos Recursos
Correções