Refactor: redesign projects hub UI, optimize workspace navigation, and harmonize feature metadata - #27
Conversation
…e WebView rendering - Replace placeholder dashboard with a theme-adaptive Projects Hub (with search, tabs, and direct shortcuts). - Sync workspace sub-tabs with TanStack Router search params to resolve linter warnings. - Harmonize creation/update date displays across all feature sidebars. - Add a "Copy Content" action to Notes and metadata row to Snippet detail views. - Fix standard CSS 'line-clamp' properties to resolve compatibility warnings. - Apply hardware acceleration layers and brightness filters to HackerLogo to resolve JavaFX WebView hover blur artifacts.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughA atualização reformula o dashboard de projetos com busca, filtros e cards interativos, sincroniza abas de workspace pela URL, amplia dados de credenciais e metadados, adiciona cópia de notas, ajusta estilos visuais e atualiza a versão do backend. ChangesDashboard e workspaces
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Pessoa
participant DashboardView
participant ProjectDetailView
participant Workspace
Pessoa->>DashboardView: pesquisa, filtra ou abre um projeto
DashboardView->>ProjectDetailView: navega para o projeto
Pessoa->>ProjectDetailView: seleciona uma aba
ProjectDetailView->>Workspace: atualiza tab na URL e renderiza a aba
Workspace-->>Pessoa: exibe conteúdo e metadados
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
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/credentials/components/CredentialDetailModal.tsx (1)
283-366: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winFallback genérico expõe segredos sem máscara e pode duplicar o valor mascarado do RAW_TEXT.
Diferente de username/password/API key/raw text (que exigem clique em "reveal"), qualquer chave customizada do payload é exibida em texto puro imediatamente pelo bloco de fallback. Além disso, quando
RAW_TEXTnão tem nenhuma das chaves nomeadas e cai no fallbackObject.values(payload)[0](linha 290), a chave real usada não está na lista de exclusão do fallback genérico — o mesmo segredo mascarado no bloco RAW_TEXT reaparece em texto puro logo abaixo.🔒 Fix sugerido: excluir a chave real usada como fonte do RAW_TEXT
+ const KNOWN_PAYLOAD_KEYS = [ + "username", "user", "email", "password", "pass", "secret", + "apiKey", "api_key", "token", "key", + "rawTextContent", "rawText", "content", "text", + ]; + const rawTextSourceKey = + cred.secretType === "RAW_TEXT" + ? ["rawTextContent", "rawText", "content", "text"].find((k) => payload[k]) ?? + Object.keys(payload)[0] + : undefined; + {/* Generic fallback for any unlisted or custom keys in payload */} {Object.entries(payload) .filter( ([key]) => - ![ - "username", "user", "email", "password", "pass", "secret", - "apiKey", "api_key", "token", "key", - "rawTextContent", "rawText", "content", "text", - ].includes(key) + !KNOWN_PAYLOAD_KEYS.includes(key) && key !== rawTextSourceKey )Também vale considerar aplicar a mesma UX de "reveal"/máscara aos campos genéricos, já que podem conter dados tão sensíveis quanto senha/API key.
🤖 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/credentials/components/CredentialDetailModal.tsx` around lines 283 - 366, Atualize o bloco de fallback genérico em conjunto com o cálculo de rawTextValue para identificar e excluir a chave efetivamente usada como fonte do RAW_TEXT, evitando duplicação do segredo. Campos genéricos também devem seguir a mesma UX de proteção: exibir valor mascarado por padrão e permitir revelar e copiar mediante ações explícitas, em vez de renderizá-los em texto puro.
🧹 Nitpick comments (11)
frontend/src/routes/__root.module.css (1)
105-116: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
transitionincluitransform, mas nada altera o transform.O único
transformé otranslateZ(0)estático e o:hoversó mudafilter, então a partetransform 0.15s easenão tem efeito. Simplifique paratransition: filter 0.15s ease;se não houver outro estado que mova o logo.🤖 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/routes/__root.module.css` around lines 105 - 116, Update the .appLogo transition declaration to animate only filter, removing transform because no state changes the logo’s transform. Preserve the existing hover brightness behavior and other styling.frontend/src/routes/index.module.css (5)
93-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLarguras mínimas fixas podem gerar overflow horizontal em telas estreitas.
.searchBoxcommin-width: 260pxe.projectsGridcomminmax(320px, 1fr)não encolhem abaixo desses valores; somados ao padding do container, estouram em viewports próximas de 320px.📱 Ajuste sugerido
.searchBox { position: relative; display: flex; align-items: center; - min-width: 260px; + min-width: min(260px, 100%); flex: 1; max-width: 440px; }.projectsGrid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 1.25rem; }Also applies to: 186-190
🤖 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/routes/index.module.css` around lines 93 - 100, Remove or replace the fixed min-width constraints in .searchBox and .projectsGrid so both can shrink on narrow viewports, while preserving their normal minimum sizes on wider screens. Account for the container padding so layouts near 320px do not overflow horizontally.
56-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFalta estilo para o estado
:disabled.
.createBtn(e.createCard, Linha 405) são renderizados comdisabled={isMutationPending}, mas sem feedback visual: o botão continua com aparência clicável ecursor: pointer.🎨 Ajuste sugerido
.createBtn:hover:not(:disabled) { transform: translateY(-1px); filter: brightness(1.12); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); } + +.createBtn:disabled { + opacity: 0.6; + cursor: not-allowed; +}🤖 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/routes/index.module.css` around lines 56 - 77, Adicionar estilos de estado disabled para .createBtn e .createCard, refletindo visualmente que não estão interativos quando isMutationPending estiver ativo. Sobrescrever o cursor pointer e ajustar opacidade/aparência, além de impedir o efeito de hover em elementos desabilitados, preservando o estilo atual nos estados habilitados.
440-444: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHover do badge ignora o estado desabilitado.
Diferente de
.createCard:hover:not(:disabled)(Linha 421), esta regra anima o badge mesmo com o botão desabilitado durante mutações.🎨 Ajuste sugerido
-.createCard:hover .createCardBadge { +.createCard:hover:not(:disabled) .createCardBadge {🤖 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/routes/index.module.css` around lines 440 - 444, Atualize o seletor `.createCard:hover .createCardBadge` para excluir cartões desabilitados, alinhando-o ao comportamento de `.createCard:hover:not(:disabled)`. Preserve a animação e as cores para cartões habilitados, mas impeça qualquer alteração do badge enquanto o botão estiver desabilitado durante mutações.
127-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCor de foco fixa contraria o restante do tema.
O comentário na Linha 79 anuncia "Theme Adaptive", mas o anel de foco usa
rgba(195, 100, 50, 0.15)fixo, que não acompanha--color-primaryem outros temas.🎨 Ajuste sugerido
.searchInput:focus { border-color: var(--color-primary); - box-shadow: 0 0 0 2px rgba(195, 100, 50, 0.15); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 15%, transparent); }🤖 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/routes/index.module.css` around lines 127 - 130, Atualize a regra .searchInput:focus para que o anel de foco derive de --color-primary em vez de usar o rgba fixo. Preserve a intensidade visual atual e garanta que o estilo acompanhe corretamente os diferentes temas.
270-288: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueClasses CSS órfãs em
index.module.css
.cardManageBtnnão é referenciada em TS/TSX e.enterArrownão é aplicada ao ícone dentro de.enterLink, então essas regras não têm efeito. Remova-as ou adicionestyles.enterArrowao ícone se a animação for necessária.🤖 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/routes/index.module.css` around lines 270 - 288, Remove the unused .cardManageBtn styles from index.module.css, and inspect the .enterArrow rule and the icon inside .enterLink: either remove the orphaned rule or apply styles.enterArrow to that icon if its animation is required.frontend/src/components/HackerLogo.module.css (2)
89-100: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
will-change+translateZ(0)são redundantes aqui.Ambos promovem a camada; manter
will-changepermanente em várias partículas simultâneas mantém camadas GPU alocadas o tempo todo, o que pesa justamente no WebView que este PR busca otimizar. Considere manter apenas um dos dois.🤖 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/HackerLogo.module.css` around lines 89 - 100, Remove the redundant layer-promotion declaration from the .cssParticle styles, keeping only either will-change: transform, opacity or transform: translateZ(0). Apply the same cleanup consistently to the dark and light theme rules without changing the particle visuals or animation behavior.
102-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winEvitar depender da ordem de
!importantentre.logoSvge.greenLogo.Como o
<svg>recebe as duas classes, odrop-shadowsó ganha porque.greenLogovem depois no arquivo. Isso deixa o efeito frágil a reorder/minificação. Remova o!importantde.logoSvgou exclua.greenLogodela.🤖 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/HackerLogo.module.css` around lines 102 - 123, Remova o !important de filter em .logoSvg, ou ajuste o seletor para não afetar elementos que também usam .greenLogo. Preserve os filtros drop-shadow definidos em .greenLogo e no seletor de tema claro, sem depender da ordem das regras CSS.frontend/src/components/DashboardView.tsx (3)
264-321: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtraia os atalhos para uma lista e renderize com
map.Os cinco
Links são idênticos exceto portab, ícone, rótulo etitle; oonClickdestopPropagationtambém está duplicado no wrapper e em cada link.♻️ Refactor sugerido
+const PROJECT_SHORTCUTS = [ + { tab: "snippets", icon: Icons.Code2, label: "Snippets", title: "Code Snippets" }, + { tab: "credentials", icon: Icons.KeyRound, label: "Vault", title: "Vault Credentials" }, + { tab: "problems", icon: Icons.AlertCircle, label: "Problems", title: "Problems & Solutions" }, + { tab: "notes", icon: Icons.FileText, label: "Notes", title: "Notes" }, + { tab: "links", icon: Icons.Link2, label: "Links", title: "Links" }, +] as const;<div className={styles.shortcutRow} onClick={(e) => e.stopPropagation()}> - <Link ... >...</Link> {/* x5 */} + {PROJECT_SHORTCUTS.map(({ tab, icon: ShortcutIcon, label, title }) => ( + <Link + key={tab} + to="/projects/$projectId" + params={{ projectId: project.id }} + search={{ tab }} + className={styles.shortcutTag} + title={title} + > + <ShortcutIcon size={11} /> + <span>{label}</span> + </Link> + ))} </div>🤖 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/DashboardView.tsx` around lines 264 - 321, Refactor the shortcut links in DashboardView’s shortcutRow by defining a data list for each tab, icon, label, and title, then render the five Links with map. Preserve the existing route, projectId params, styling, icons, labels, titles, and stopPropagation behavior, removing the duplicated JSX while retaining the wrapper’s propagation handling.
121-139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdicione um rótulo acessível ao campo de busca.
O
inputsó templaceholder, que não é anunciado como rótulo por leitores de tela; o botão de limpar também não tem nome acessível.♿ Correção sugerida
<input type="text" className={styles.searchInput} + aria-label="Filtrar projetos" placeholder="Filter projects by name or description..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> {searchQuery && ( <button type="button" className={styles.clearSearchBtn} + aria-label="Limpar busca" onClick={() => setSearchQuery("")} >🤖 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/DashboardView.tsx` around lines 121 - 139, Update the search input in DashboardView to include a persistent accessible label, and add an accessible name to the clearSearchBtn button so screen readers identify both controls without relying on placeholder text or icons.
215-236: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftCard
role="button"com links/botões aninhados quebra a semântica de acessibilidade.Um elemento com
role="button"não pode conter conteúdo interativo (osLinks de atalho e os botões do rodapé). Leitores de tela achatam o conteúdo e a ordem de foco fica ambígua. Uma alternativa comum é manter o card comodivnão focável e transformar o título em umLinkreal (padrão "card com link estendido" via::after), preservando oonClickno card apenas como conveniência de mouse.🤖 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/DashboardView.tsx` around lines 215 - 236, Remove the card’s role="button" and tabIndex so the project card remains a non-focusable div when it contains nested links and footer buttons. Keep its onClick navigation as mouse convenience, and move keyboard/focus navigation to the project title by making it a real Link, using the existing project route and preserving the nested controls’ independent interaction.
🤖 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/components/DashboardView.tsx`:
- Around line 210-250: Update the cardIconBox borderColor and backgroundColor
styling in the filteredProjects mapping to derive transparency with CSS
color-mix() instead of appending a hex alpha suffix to cardColor. Preserve
cardColor for the accent bar and icon, and ensure the new color-mix values work
with fallback, rgb(), named, and hex color formats.
- Around line 141-165: Update the DashboardView tab-selection logic so tabFilter
cannot remain effectively set to "ARCHIVED" when archivedProjectsCount is zero;
derive an effective tab that falls back to a visible filter, and use
effectiveTab for filterTabActive comparisons and empty-state text while
preserving the existing tabFilter controls.
In `@frontend/src/components/ProjectDetailView.tsx`:
- Around line 16-23: Remova a importação de Route de
../routes/projects.$projectId em ProjectDetailView e use
getRouteApi("/projects/$projectId") para criar a API da rota. Substitua as
chamadas Route.useSearch() e Route.useNavigate() por routeApi.useSearch() e
routeApi.useNavigate(), preservando o comportamento atual e eliminando a
dependência circular.
In `@frontend/src/features/notes/components/NotesWorkspace.tsx`:
- Around line 336-353: Update the Copy button handler in NotesWorkspace to
choose inlineContent when isEditingContent is true, and otherwise use
noteDetail.content. Preserve the existing empty-content guard, clipboard call,
and success/error toast behavior while ensuring unsaved inline edits are copied.
---
Outside diff comments:
In `@frontend/src/features/credentials/components/CredentialDetailModal.tsx`:
- Around line 283-366: Atualize o bloco de fallback genérico em conjunto com o
cálculo de rawTextValue para identificar e excluir a chave efetivamente usada
como fonte do RAW_TEXT, evitando duplicação do segredo. Campos genéricos também
devem seguir a mesma UX de proteção: exibir valor mascarado por padrão e
permitir revelar e copiar mediante ações explícitas, em vez de renderizá-los em
texto puro.
---
Nitpick comments:
In `@frontend/src/components/DashboardView.tsx`:
- Around line 264-321: Refactor the shortcut links in DashboardView’s
shortcutRow by defining a data list for each tab, icon, label, and title, then
render the five Links with map. Preserve the existing route, projectId params,
styling, icons, labels, titles, and stopPropagation behavior, removing the
duplicated JSX while retaining the wrapper’s propagation handling.
- Around line 121-139: Update the search input in DashboardView to include a
persistent accessible label, and add an accessible name to the clearSearchBtn
button so screen readers identify both controls without relying on placeholder
text or icons.
- Around line 215-236: Remove the card’s role="button" and tabIndex so the
project card remains a non-focusable div when it contains nested links and
footer buttons. Keep its onClick navigation as mouse convenience, and move
keyboard/focus navigation to the project title by making it a real Link, using
the existing project route and preserving the nested controls’ independent
interaction.
In `@frontend/src/components/HackerLogo.module.css`:
- Around line 89-100: Remove the redundant layer-promotion declaration from the
.cssParticle styles, keeping only either will-change: transform, opacity or
transform: translateZ(0). Apply the same cleanup consistently to the dark and
light theme rules without changing the particle visuals or animation behavior.
- Around line 102-123: Remova o !important de filter em .logoSvg, ou ajuste o
seletor para não afetar elementos que também usam .greenLogo. Preserve os
filtros drop-shadow definidos em .greenLogo e no seletor de tema claro, sem
depender da ordem das regras CSS.
In `@frontend/src/routes/__root.module.css`:
- Around line 105-116: Update the .appLogo transition declaration to animate
only filter, removing transform because no state changes the logo’s transform.
Preserve the existing hover brightness behavior and other styling.
In `@frontend/src/routes/index.module.css`:
- Around line 93-100: Remove or replace the fixed min-width constraints in
.searchBox and .projectsGrid so both can shrink on narrow viewports, while
preserving their normal minimum sizes on wider screens. Account for the
container padding so layouts near 320px do not overflow horizontally.
- Around line 56-77: Adicionar estilos de estado disabled para .createBtn e
.createCard, refletindo visualmente que não estão interativos quando
isMutationPending estiver ativo. Sobrescrever o cursor pointer e ajustar
opacidade/aparência, além de impedir o efeito de hover em elementos
desabilitados, preservando o estilo atual nos estados habilitados.
- Around line 440-444: Atualize o seletor `.createCard:hover .createCardBadge`
para excluir cartões desabilitados, alinhando-o ao comportamento de
`.createCard:hover:not(:disabled)`. Preserve a animação e as cores para cartões
habilitados, mas impeça qualquer alteração do badge enquanto o botão estiver
desabilitado durante mutações.
- Around line 127-130: Atualize a regra .searchInput:focus para que o anel de
foco derive de --color-primary em vez de usar o rgba fixo. Preserve a
intensidade visual atual e garanta que o estilo acompanhe corretamente os
diferentes temas.
- Around line 270-288: Remove the unused .cardManageBtn styles from
index.module.css, and inspect the .enterArrow rule and the icon inside
.enterLink: either remove the orphaned rule or apply styles.enterArrow to that
icon if its animation is required.
🪄 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: 91025539-1b81-418d-8435-4239027a8336
📒 Files selected for processing (15)
backend/src/main/resources/application.yamlfrontend/src/components/DashboardView.tsxfrontend/src/components/HackerLogo.module.cssfrontend/src/components/ProjectDetailView.tsxfrontend/src/components/TagManagerSection.tsxfrontend/src/features/credentials/components/CredentialDetailModal.tsxfrontend/src/features/credentials/components/CredentialsWorkspace.tsxfrontend/src/features/links/components/LinksWorkspace.tsxfrontend/src/features/notes/components/NotesWorkspace.tsxfrontend/src/features/snippets/components/SnippetsWorkspace.tsxfrontend/src/routes/__root.module.cssfrontend/src/routes/index.module.cssfrontend/src/routes/projects.$projectId.module.cssfrontend/src/routes/projects.$projectId.tsxfrontend/src/types/api.ts
…uter usage, and enable copying active note edits
Description
This PR refactors the home dashboard page into a premium, theme-adaptive Projects Hub, resolves workspace feature tab sub-navigation, harmonizes sidebar headers/dates, and adds quality-of-life enhancements for Snippets, Notes, and desktop performance.
Key Changes
"View Details"settings trigger.Snippets,Vault,Problems,Notes,Links).validateSearchfor thetabURL parameter in TanStack Router.toLocaleDateString()).align-items: flex-startwith clean spacing so titles wrap gracefully without crowding or overlapping date tags.copyToClipboard.CreatedandUpdateddates under the snippet detail title (consistent with other workspaces).line-clampproperties alongside vendor prefixes inindex.module.cssandprojects.$projectId.module.css.transform: translateZ(0),backface-visibility: hidden) on.appLogoand.greenLogocomponents.opacitytransitions withfilter: brightness(1.1)to bypass subpixel antialiasing smearing and downsampling issues.Verification
npm run buildwhich compiled successfully with 0 errors and 0 warnings.Summary by CodeRabbit