mobile: table view, filters, and row-edit overhaul#1814
Conversation
…s cleanup - Table view: card-style rows on mobile (rounded border, dashed inter-cell separators, label/value centered between dashes, checkbox+actions strip non-clickable for preview), unified icon button styling, mobile-only Sort button on the saved-filters row. - Saved filters: combined dropdown trigger on mobile (active filter shown with check + bold name, "New fast filter" entry at the bottom); desktop chip layout preserved. Applied-filter details get a soft background and no longer trigger horizontal overflow. - Row preview: bottom sheet on mobile with backdrop scrim, drag handle, safe-area padding. Related records flattened (outer expansion removed) and per-row navigation moved to an explicit chevron button to prevent accidental teleports. - Mobile actions: icon-only with tooltips, unified outlined symbol set, count badge hidden when label collapses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Empty-filter state: card-style placeholder with icon + tailored copy for fast filters vs column filters; separate from the empty-table CTA. - Select all: moved from settings menu into the bulk-actions toolbar so it appears after the first row is selected. - Search: collapsed pill (40x40 with search icon) that expands on focus or when filled — saves room next to the action buttons. - Action icons: unified to material-symbols-outlined and bumped to 22px with stronger color to match the checkbox weight on cards. - Saved filters menu: fixed nested-button alignment by replacing the per-row trigger button with a span; check icon kept in flow via visibility toggle so labels stay vertically centered. - Filter dialog: opens as a bottom sheet on mobile via panelClass with rounded top corners, drag handle, slide-up animation, safe-area pad. - Comparator selects: narrowed (max 116px) and dropdown panel widened to its content (max 100vw - 32px) so long options stay readable. - Foreign-key filter: subscript wrapper hidden globally so the field is no longer artificially tall. - Number cell: left-aligned in mobile cards (copy button moved back to the right edge) so values line up with the rest. - Logo: short rocketadmin icon swapped in on mobile via <picture>. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s on mobile - Floating labels in filter form fields: removed max-width / overflow clip on .mdc-notched-outline__notch and .mdc-floating-label so the text isn't truncated when the field is narrow; .filters-container switched from overflow: hidden to overflow-x: clip / overflow-y: visible to stop vertical clipping of the rising label. - Filter dialog on mobile: each filter row now stacks column name above the inputs as a small caption (12px, 600, .6 opacity) via grid-template-areas instead of a 3-column inline layout. - Filter dialog spacing: added breathing room between the "Filters for [table]" header and the first "Add filter by..." field so its floating label no longer collides with the title (both desktop and bottom-sheet mobile variant). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Each filter condition is now a stacked block with column name caption, comparator + value on one row, and a "Quick editing" toggle below; conditions are separated by a dashed line (matching the table card style) instead of a gray background. - Comparator dropdown collapses to a single-symbol trigger (=, >, ≥, etc. for numbers; a…, …a, ∋, ∌, ∅ for text) and is locked to 56px with inner Material padding/min-width / subscript-wrapper all stripped, so the value field can take the rest of the row. - Value input wrapped in .condition-card__value to ensure the dynamic filter component (ndc-dynamic host) stretches to fill remaining space — eliminates the dead gap between comparator and value field. - Dialog header restructured: small "New fast filter" / "Edit fast filter" label above a smaller table name; mat-dialog-title's MDC baseline ::before spacer disabled so the heading height matches the regular filter dialog. - Dialog actions (Save / Cancel) are sticky at the bottom; surface uses overflow: hidden with a scrollable .filters-content area so buttons stay in view instead of scrolling off. - info_outline help icon (with the existing animated quick-edit explanation menu) is rendered next to the per-condition toggle; the global "Quick edit" header block was removed and the mat-menu hoisted out of *ngIf so the per-row triggers resolve it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Table switcher: replaced the mat-menu (whose search overlapped the trigger and caused misclicks) with a bottom sheet — search pill, collapsible folders with colored folder icons (dehaze for All tables), active table checkmark, slide-up + scrim. Rendered at the component root with backdrop-filter/translateZ so the AI-config banner's backdrop-filter no longer composites over it. - Sort by: bottom sheet listing sortable columns; per-column asc/desc icon buttons (active highlighted) plus a lock toggle for set/remove default — replaces the awkward nested submenu. - Columns: opens the same drag-drop checkbox list as a bottom sheet on mobile (isMobileView nulls the matMenuTriggerFor) while desktop keeps the dropdown menu. - Comparator selects in filter dialogs show full text on desktop and a single symbol on mobile; table-name shown as "Table: <name>" in both filter dialog headers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ctions - Table view: removed the refresh icon on mobile in favor of pull-to- refresh (touch handlers trigger loadRowsPage past a 70px pull with a spinner indicator); Import/Export opens as a bottom sheet on mobile while desktop keeps the dropdown menu. - Row preview: wider 20px side padding, header trailing icons aligned, copy-link snackbar shown at the top so it isn't hidden under the sheet. - Cell copy buttons hidden on mobile — they were invisible (no hover) but still tappable, causing accidental copies instead of opening the row preview. - Row edit form: action bar (Back / Save / Save and continue editing) fixed to the viewport bottom on mobile so it no longer scrolls off the page; the form scroll container is mat-drawer-content and position: fixed correctly pins (verified — no transformed ancestor), so a flex static footer is NOT used. Buttons reflow: Back+Save in a row, the long continue button on its own line; safe-area padding at the bottom. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…it form - Table view header on mobile: a 12px gap separates the table-switcher row from the search; the AI icon and Settings gear moved to the table-switcher row (far right) so the actions row stays uncluttered. - Filter actions split out of the icon-only actions row: Filter is a small outlined button under a full-width search field; Add row turned into an extended FAB (icon + label) pinned to the bottom-right, shown whenever the user has add permission (no longer hidden on empty tables) so it never disappears behind the empty-state CTA. - Columns moved next to Sort by in the saved-filters row, with the saved-filters-row switched from absolute Sort to a flex layout so Columns + Sort sit side-by-side at the right; both are icon-only on mobile (Columns view_week, Sort by gets a leading swap_vert icon). The actions-row originals are hidden on mobile. - Sort bottom sheet redesigned: tapping a column expands inline buttons for Ascending / Descending and a Set/Remove default action with a pin icon; an active sort arrow is appended to the column data-label in row cards. - Import/Export consolidated into the Settings menu on mobile (with a divider) so the separate swap_vert button can be hidden. - Filter dialog: autoFocus disabled so the "Add filter by..." input doesn't auto-open the column dropdown; settings icon explicitly pushed to the right via margin-left:auto. - Row edit form: breadcrumbs replaced with a back arrow and a two-line title (page mode + table name); page top margin reduced and empty app-alert collapsed so the title sits higher; widget info icon next to fields removed. Action bar reworked — Back removed entirely (the header arrow handles it); Save & continue pushed left, primary submit (Save / Add / Edit / Duplicate) right; on mobile the bar is a fixed bottom bar with safe-area padding and the buttons reflow with Save & continue on its own full-width row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
📝 WalkthroughWalkthroughThis PR introduces a comprehensive mobile-first redesign of the frontend dashboard and data management interfaces. The changes span responsive layouts, touch interactions, sheet-based dialogs, and responsive typography across the table view, filters, row editing, and supporting components, targeting viewports ≤600px with safe-area awareness and bottom-sheet pattern adoption. ChangesMobile-first Dashboard and Data Management UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
🧹 Nitpick comments (2)
frontend/src/app/services/notifications.service.ts (1)
21-27: 💤 Low valueAdd an explicit return type to the method.
The signature change to add an optional
verticalPositionwith a default is backward compatible with existing callers. Per the TS guideline, add a: voidreturn annotation.📝 Proposed fix
- showSuccessSnackbar(message: string, verticalPosition: 'top' | 'bottom' = 'bottom') { + showSuccessSnackbar(message: string, verticalPosition: 'top' | 'bottom' = 'bottom'): void {As per coding guidelines: "Always add type annotations to function parameters and return types in TypeScript".
🤖 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/app/services/notifications.service.ts` around lines 21 - 27, The method showSuccessSnackbar in notifications.service.ts is missing an explicit return type; update its signature to include a void return annotation (e.g., change the declaration of showSuccessSnackbar(message: string, verticalPosition: 'top' | 'bottom' = 'bottom') to include : void) so it conforms to the TypeScript guidelines requiring explicit function return types.frontend/src/styles.scss (1)
126-126: 💤 Low valueReference palette CSS variables instead of hardcoding colors.
The box-shadow (Line 126) and drag-handle backgrounds (Lines 156, 163) use hardcoded
rgba(...)values. Consider sourcing these from the generated palette so they stay theme-consistent.As per coding guidelines: "Only use colors from generated palettes defined in
src/main.ts(colorConfig). Do not hardcode arbitrary color values; reference palette CSS variables from@brumeilde/ngx-theme".Also applies to: 156-156, 163-163
🤖 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/styles.scss` at line 126, Replace the hardcoded rgba(...) values in styles.scss (the box-shadow declaration and the drag-handle background declarations) with the generated palette CSS variables produced by your colorConfig in src/main.ts and exposed by `@brumeilde/ngx-theme`; locate the box-shadow line and the drag-handle rules (background properties) and swap the rgba literals for the appropriate palette variables (e.g., var(--palette-<color>-<shade>) or whatever naming your build emits), ensuring you reference the same generated variable names used by `@brumeilde/ngx-theme` so the shadow and handle colors follow the active theme.
🤖 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/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.css`:
- Around line 84-91: The `@keyframes` name rowPreviewSlideUp uses camelCase and
breaks the configured keyframes-name-pattern; rename it to kebab-case (e.g.,
row-preview-slide-up) and update all corresponding usages (animation,
animation-name, and any JS/TS references) to the new identifier; repeat the same
change for the other keyframes defined at 93-100 (rename to a kebab-case
equivalent) so stylelint passes and all animations still reference the updated
names (check CSS selectors, inline styles, and component templates).
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.html`:
- Around line 1-4: The template still uses legacy structural directives (*ngIf,
*ngFor, *ngSwitch) in newly edited blocks (e.g., the div with class
"row-preview-backdrop" that binds to selectedRow and calls handleClose()), which
violates the Angular 19 control-flow guideline; update those instances to use
the built-in control-flow syntax (`@if`, `@for`, `@switch`) — for example replace the
*ngIf usage on the preview/backdrop and any other occurrences (including the
blocks referenced around the row rendering and switch logic) with
`@if`="selectedRow" and convert any *ngFor/*ngSwitch blocks to `@for/`@switch
equivalents while keeping existing event handlers and classes (e.g.,
handleClose, selectedRow, row-preview-backdrop) intact.
- Around line 73-80: The icon-only anchor with class "related-record__open" (the
<a matListItemMeta mat-icon-button ...> that navigates using [routerLink] and
calls handleClose()) needs an explicit accessible label; add an aria-label (or
[attr.aria-label] bound) to that anchor — e.g. a static "Open record" or a
dynamic label like `Open record for {{ referencedTable.table_name }}` or
including the referenced record id from
referencedRecords[referencedTable.table_name]?.links[i] — so screen readers get
a clear action name while preserving the existing routerLink, [queryParams], and
(click)="handleClose()".
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css`:
- Line 33: Rename the camelCase keyframe name pullRefreshSpin to a kebab-case
name (e.g., pull-refresh-spin) and update all usages accordingly; locate the
`@keyframes` declaration for pullRefreshSpin and change its identifier, then find
any animation, animation-name, or shorthand references (the occurrences
currently at the other two locations reported) and replace pullRefreshSpin with
the new kebab-case identifier so linting passes.
- Line 1113: In db-table-view.component.css replace the deprecated grid-gap
property usage with the modern gap property (i.e., change the rule that
currently sets "grid-gap: 2px 12px;" to use "gap: 2px 12px;") so the CSS uses
current syntax and satisfies lint rules; update any identical occurrences of
grid-gap in the same file or component stylesheet (search for "grid-gap" to
locate all instances).
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html`:
- Around line 1-9: The template uses legacy structural directives; replace all
*ngIf/*ngFor/*ngSwitch usages in this component with Angular 19 built-in
control-flow directives (`@if`, `@for`, `@switch`). Concretely, update the
pull-to-refresh indicator markup to use `@if/`@for patterns where conditional
rendering depends on pullDistance and pullRefreshing (the div with class
pull-refresh-indicator and the mat-icon), and apply the same conversion for the
table switcher sheet, sort sheet, columns sheet, and transfer sheet (the
sections referenced in the review). Keep the existing bindings (e.g.,
[class.pull-refresh-indicator_active], [style.height.px], [style.transform])
intact while replacing the structural directives, and run template linting to
ensure the new `@if/`@for/@switch syntax is used consistently.
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts`:
- Line 315: Convert the listed component state fields into Angular signals:
replace the plain fields tableSwitcherSearch, tableSwitcherOpen,
collapsedFolders, sortSheetOpen, sortExpandedColumn, columnsSheetOpen,
transferSheetOpen, pullDistance, and pullRefreshing with protected or private
readonly signals initialized via signal(...) (e.g., protected readonly
tableSwitcherSearch = signal('') or appropriate initial values). Update all
writes to these states to use .set(...) (e.g., tableSwitcherSearch.set(...)) and
update reads in methods and templates to use signal reads (call the signal or
use its value per your project convention). Ensure the symbol names remain
identical and update any usages in methods like anyTableSwitching handlers,
folder toggles, sort/column sheet handlers, transfer sheet logic, and
pull-to-refresh handlers to use the new signal API.
- Around line 356-358: The isMobileView getter directly reads window.innerWidth
which risks SSR hydration issues; update the component to perform a proper
platform check using Angular's PLATFORM_ID and isPlatformBrowser (inject
PLATFORM_ID into the component constructor and call
isPlatformBrowser(this.platformId) before accessing window) or switch to Angular
CDK's BreakpointObserver/responsive service to determine mobile state without
touching window during server render; modify the isMobileView getter (or replace
it with a boolean property updated by BreakpointObserver) so any window access
is gated by isPlatformBrowser(this.platformId) and ensure the constructor
injects PLATFORM_ID (and BreakpointObserver if used).
In
`@frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css`:
- Line 876: Replace the deprecated CSS property "grid-row-gap" with the standard
"row-gap" in the saved filters dialog stylesheet: find the rule that sets
"grid-row-gap: 0 !important;" (in saved-filters-dialog.component.css) and change
it to use "row-gap: 0 !important;" so stylelint no longer flags the deprecated
property.
In
`@frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.html`:
- Around line 81-238: The template uses structural directives (*ngFor, *ngIf) —
replace them with Angular 19 control-flow syntax: convert the outer loop over
tableRowFieldsShown (currently on the div with class "condition-card") to
`@for`="let value of tableRowFieldsShown | keyvalue; trackBy: trackByFn", and
replace each *ngIf (checks calling getComparatorType(getInputType(value.key)),
isWidget(value.key), and the type-specific comparator checks) with `@if`
expressions and `@else` references that preserve the existing ng-template
fallbacks (defaultTableField and comparableFilterInputs). Ensure the ndc-dynamic
usages, bindings to tableRowFieldsShown/tableRowFieldsComparator, and event
handlers (updateField, updateComparatorFromComponent, updateComparator) remain
unchanged, and keep the quickEditHelpMenu/ toggleDynamicColumn logic intact.
In
`@frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.html`:
- Around line 44-81: Replace the new template usages of structural directives
with Angular 19 control-flow syntax: wrap the menu trigger block that currently
uses *ngIf="savedFilterData.length !== 0" in an <ng-container> using `@if` with
the same condition; change the *ngFor on the menu item to an <ng-container>
using `@for`="let filter of savedFilterData" (keeping the same bindings inside the
generated button), and replace the other *ngIf (canEditConnection()) with `@if` on
an <ng-container> so canEditConnection(), selectFiltersSet(filter.id),
setCurrentFilter(filter), selectedFilterSetId, savedFilterMap and
handleOpenSavedFiltersDialog() continue to be referenced unchanged; ensure you
stopPropagation() where you did before and preserve matMenuTriggerFor bindings
and classes.
- Around line 64-69: Replace the clickable <span> used as the per-row menu
trigger with a real <button> (e.g., mat-icon-button) to restore keyboard
accessibility and correct semantics: locate the template block that calls
canEditConnection() and uses [matMenuTriggerFor]="filterMenu" and
(click)="$event.stopPropagation(); setCurrentFilter(filter)" (the
saved-filters-menu__row-menu element) and change it to a button element, keep
the matMenuTriggerFor binding and the click handler (including stopPropagation
and setCurrentFilter(filter)), add type="button" and an appropriate aria-label,
and preserve the mat-icon and CSS class so styling/behavior remain the same.
In
`@frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.html`:
- Around line 24-30: The back link element (anchor with class
"row-edit-header__back" and data-testid "record-back-to-table-button") is
icon-only and needs an accessible name; add an aria-label (e.g.,
aria-label="Back to table" or use a localized label) to the anchor so screen
readers announce its purpose, keeping the existing routerLink, [queryParams],
matTooltip and mat-icon intact.
In `@frontend/src/styles.scss`:
- Around line 118-120: Insert a blank line between the custom-property
declaration --mdc-dialog-container-shape and the subsequent declaration
max-height to satisfy Stylelint's declaration-empty-line-before rule; locate the
rule block containing --mdc-dialog-container-shape, max-height, and
padding-bottom and add a single empty line before the max-height declaration so
the sequence is: --mdc-dialog-container-shape, (blank line), max-height,
padding-bottom.
- Around line 167-174: The keyframes identifier mobileBottomSheetSlideUp must be
renamed to kebab-case (e.g., mobile-bottom-sheet-slide-up) and every reference
to it in the stylesheet updated accordingly; specifically rename the `@keyframes`
declaration name and update any animation/animation-name/shorthand properties
that currently reference mobileBottomSheetSlideUp so they use the new kebab-case
name (ensure exact string match for shorthand usages).
---
Nitpick comments:
In `@frontend/src/app/services/notifications.service.ts`:
- Around line 21-27: The method showSuccessSnackbar in notifications.service.ts
is missing an explicit return type; update its signature to include a void
return annotation (e.g., change the declaration of showSuccessSnackbar(message:
string, verticalPosition: 'top' | 'bottom' = 'bottom') to include : void) so it
conforms to the TypeScript guidelines requiring explicit function return types.
In `@frontend/src/styles.scss`:
- Line 126: Replace the hardcoded rgba(...) values in styles.scss (the
box-shadow declaration and the drag-handle background declarations) with the
generated palette CSS variables produced by your colorConfig in src/main.ts and
exposed by `@brumeilde/ngx-theme`; locate the box-shadow line and the drag-handle
rules (background properties) and swap the rgba literals for the appropriate
palette variables (e.g., var(--palette-<color>-<shade>) or whatever naming your
build emits), ensuring you reference the same generated variable names used by
`@brumeilde/ngx-theme` so the shadow and handle colors follow the active theme.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 213eb3f2-a173-4804-ac1e-c742d8b1b75c
📒 Files selected for processing (25)
frontend/src/app/app.component.cssfrontend/src/app/app.component.htmlfrontend/src/app/components/dashboard/dashboard.component.tsfrontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.htmlfrontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.htmlfrontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.tsfrontend/src/app/components/dashboard/db-table-view/db-table-view.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-view.component.htmlfrontend/src/app/components/dashboard/db-table-view/db-table-view.component.tsfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.cssfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.htmlfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.cssfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.htmlfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.tsfrontend/src/app/components/db-table-row-edit/db-table-row-edit.component.cssfrontend/src/app/components/db-table-row-edit/db-table-row-edit.component.htmlfrontend/src/app/components/db-table-row-edit/db-table-row-edit.component.tsfrontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.cssfrontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.htmlfrontend/src/app/components/ui-components/table-display-fields/base-table-display-field/base-table-display-field.component.cssfrontend/src/app/components/ui-components/table-display-fields/number/number.component.cssfrontend/src/app/services/notifications.service.tsfrontend/src/styles.scss
💤 Files with no reviewable changes (1)
- frontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.html
| @keyframes rowPreviewSlideUp { | ||
| from { | ||
| transform: translateY(100%); | ||
| } | ||
| to { | ||
| transform: translateY(0); | ||
| } | ||
| } |
There was a problem hiding this comment.
Rename keyframes to kebab-case to satisfy stylelint.
The new animation names violate the configured keyframes-name-pattern and can fail lint checks.
Suggested fix
- animation: rowPreviewSlideUp 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
+ animation: row-preview-slide-up 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
...
- animation: rowPreviewBackdropFadeIn 240ms ease-out;
+ animation: row-preview-backdrop-fade-in 240ms ease-out;
...
-@keyframes rowPreviewSlideUp {
+@keyframes row-preview-slide-up {
...
-@keyframes rowPreviewBackdropFadeIn {
+@keyframes row-preview-backdrop-fade-in {Also applies to: 93-100
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 84-84: Expected keyframe name "rowPreviewSlideUp" to be kebab-case (keyframes-name-pattern)
(keyframes-name-pattern)
🤖 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/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.css`
around lines 84 - 91, The `@keyframes` name rowPreviewSlideUp uses camelCase and
breaks the configured keyframes-name-pattern; rename it to kebab-case (e.g.,
row-preview-slide-up) and update all corresponding usages (animation,
animation-name, and any JS/TS references) to the new identifier; repeat the same
change for the other keyframes defined at 93-100 (rename to a kebab-case
equivalent) so stylelint passes and all animations still reference the updated
names (check CSS selectors, inline styles, and component templates).
| <div *ngIf="selectedRow" | ||
| class="row-preview-backdrop" | ||
| (click)="handleClose()"></div> | ||
| <div *ngIf="selectedRow" |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
Migrate newly touched structural directives to Angular built-in control flow.
The changed template still uses *ngIf/*ngFor in newly modified blocks; this conflicts with the frontend template standard for new code.
As per coding guidelines, "Use Angular 19's built-in control flow (@if, @for, @switch) instead of structural directives (*ngIf, *ngFor, *ngSwitch) in all new code".
Also applies to: 30-35, 57-59
🧰 Tools
🪛 HTMLHint (1.9.2)
[error] 1-1: Doctype must be declared before any non-comment content.
(doctype-first)
🤖 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/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.html`
around lines 1 - 4, The template still uses legacy structural directives (*ngIf,
*ngFor, *ngSwitch) in newly edited blocks (e.g., the div with class
"row-preview-backdrop" that binds to selectedRow and calls handleClose()), which
violates the Angular 19 control-flow guideline; update those instances to use
the built-in control-flow syntax (`@if`, `@for`, `@switch`) — for example replace the
*ngIf usage on the preview/backdrop and any other occurrences (including the
blocks referenced around the row rendering and switch logic) with
`@if`="selectedRow" and convert any *ngFor/*ngSwitch blocks to `@for/`@switch
equivalents while keeping existing event handlers and classes (e.g.,
handleClose, selectedRow, row-preview-backdrop) intact.
| <a matListItemMeta mat-icon-button | ||
| class="related-record__open" | ||
| matTooltip="Open record" | ||
| [routerLink]="['/dashboard', selectedRow.connectionID, referencedTable.table_name, 'entry']" | ||
| [queryParams]="referencedRecords[referencedTable.table_name]?.links[i]" | ||
| (click)="handleClose()"> | ||
| <mat-icon>chevron_right</mat-icon> | ||
| </a> |
There was a problem hiding this comment.
Add an explicit accessible label to the icon-only “open record” action.
This anchor is icon-only; add aria-label so screen-reader users get a clear action name.
Suggested fix
- <a matListItemMeta mat-icon-button
+ <a matListItemMeta mat-icon-button
+ aria-label="Open related record"
class="related-record__open"
matTooltip="Open record"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a matListItemMeta mat-icon-button | |
| class="related-record__open" | |
| matTooltip="Open record" | |
| [routerLink]="['/dashboard', selectedRow.connectionID, referencedTable.table_name, 'entry']" | |
| [queryParams]="referencedRecords[referencedTable.table_name]?.links[i]" | |
| (click)="handleClose()"> | |
| <mat-icon>chevron_right</mat-icon> | |
| </a> | |
| <a matListItemMeta mat-icon-button | |
| aria-label="Open related record" | |
| class="related-record__open" | |
| matTooltip="Open record" | |
| [routerLink]="['/dashboard', selectedRow.connectionID, referencedTable.table_name, 'entry']" | |
| [queryParams]="referencedRecords[referencedTable.table_name]?.links[i]" | |
| (click)="handleClose()"> | |
| <mat-icon>chevron_right</mat-icon> | |
| </a> |
🤖 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/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.html`
around lines 73 - 80, The icon-only anchor with class "related-record__open"
(the <a matListItemMeta mat-icon-button ...> that navigates using [routerLink]
and calls handleClose()) needs an explicit accessible label; add an aria-label
(or [attr.aria-label] bound) to that anchor — e.g. a static "Open record" or a
dynamic label like `Open record for {{ referencedTable.table_name }}` or
including the referenced record id from
referencedRecords[referencedTable.table_name]?.links[i] — so screen readers get
a clear action name while preserving the existing routerLink, [queryParams], and
(click)="handleClose()".
| } | ||
| } | ||
|
|
||
| @keyframes pullRefreshSpin { |
There was a problem hiding this comment.
Rename keyframes to kebab-case to satisfy stylelint
Line 33, Line 510, and Line 519 use camelCase keyframe names, which violates the configured keyframe naming rule and can fail linting.
Suggested patch
-@keyframes pullRefreshSpin {
+@keyframes pull-refresh-spin {
@@
- .pull-refresh-indicator__icon_spinning {
- animation: pullRefreshSpin 700ms linear infinite;
+ .pull-refresh-indicator__icon_spinning {
+ animation: pull-refresh-spin 700ms linear infinite;
}-@keyframes tableSheetSlideUp {
+@keyframes table-sheet-slide-up {
@@
-@keyframes tableSheetFadeIn {
+@keyframes table-sheet-fade-in {
@@
-.table-sheet-backdrop {
+.table-sheet-backdrop {
...
- animation: tableSheetFadeIn 200ms ease-out;
+ animation: table-sheet-fade-in 200ms ease-out;
}
@@
-.table-sheet {
+.table-sheet {
...
- animation: tableSheetSlideUp 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
+ animation: table-sheet-slide-up 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
}Also applies to: 510-510, 519-519
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 33-33: Expected keyframe name "pullRefreshSpin" to be kebab-case (keyframes-name-pattern)
(keyframes-name-pattern)
🤖 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/app/components/dashboard/db-table-view/db-table-view.component.css`
at line 33, Rename the camelCase keyframe name pullRefreshSpin to a kebab-case
name (e.g., pull-refresh-spin) and update all usages accordingly; locate the
`@keyframes` declaration for pullRefreshSpin and change its identifier, then find
any animation, animation-name, or shorthand references (the occurrences
currently at the other two locations reported) and replace pullRefreshSpin with
the new kebab-case identifier so linting passes.
| border-bottom-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12)); | ||
| border-bottom-width: var(--mat-table-row-item-outline-width, 1px); | ||
| border-bottom-style: solid; | ||
| grid-gap: 2px 12px; |
There was a problem hiding this comment.
Replace deprecated grid-gap with gap
Line 1113 uses deprecated grid-gap; switch to gap to satisfy lint rules and modern CSS usage.
Suggested patch
- grid-gap: 2px 12px;
+ gap: 2px 12px;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| grid-gap: 2px 12px; | |
| gap: 2px 12px; |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 1113-1113: Expected "grid-gap" to be "gap" (property-no-deprecated)
(property-no-deprecated)
🤖 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/app/components/dashboard/db-table-view/db-table-view.component.css`
at line 1113, In db-table-view.component.css replace the deprecated grid-gap
property usage with the modern gap property (i.e., change the rule that
currently sets "grid-gap: 2px 12px;" to use "gap: 2px 12px;") so the CSS uses
current syntax and satisfies lint rules; update any identical occurrences of
grid-gap in the same file or component stylesheet (search for "grid-gap" to
locate all instances).
| <button *ngIf="savedFilterData.length !== 0" | ||
| mat-stroked-button type="button" | ||
| class="saved-filters-trigger" | ||
| [class.saved-filters-trigger_active]="selectedFilterSetId" | ||
| [matMenuTriggerFor]="filtersMenu"> | ||
| <mat-icon fontSet="material-symbols-outlined" class="saved-filters-trigger__icon">tune</mat-icon> | ||
| <span class="saved-filters-trigger__label"> | ||
| {{ savedFilterMap[selectedFilterSetId]?.name || 'Fast filters' }} | ||
| </span> | ||
| <mat-icon iconPositionEnd class="saved-filters-trigger__chevron">arrow_drop_down</mat-icon> | ||
| </button> | ||
|
|
||
| <mat-menu #filtersMenu="matMenu" class="saved-filters-menu"> | ||
| <button mat-menu-item type="button" | ||
| *ngFor="let filter of savedFilterData" | ||
| [class.saved-filters-menu__item_active]="filter.id === selectedFilterSetId" | ||
| (click)="selectFiltersSet(filter.id)"> | ||
| <mat-icon class="saved-filters-menu__check" | ||
| [class.saved-filters-menu__check_visible]="filter.id === selectedFilterSetId">check</mat-icon> | ||
| <span class="saved-filters-menu__name">{{ filter.name }}</span> | ||
| <span *ngIf="canEditConnection()" | ||
| class="saved-filters-menu__row-menu" | ||
| [matMenuTriggerFor]="filterMenu" | ||
| (click)="$event.stopPropagation(); setCurrentFilter(filter)"> | ||
| <mat-icon>more_vert</mat-icon> | ||
| </span> | ||
| </button> | ||
|
|
||
| <button *ngIf="canEditConnection()" | ||
| mat-menu-item type="button" | ||
| class="saved-filters-menu__create" | ||
| angulartics2On="click" | ||
| angularticsAction="Saved filters: create new filter is clicked" | ||
| (click)="handleOpenSavedFiltersDialog(); posthog.capture('Saved filters: create new filter is clicked')"> | ||
| <mat-icon>bookmark_add</mat-icon> | ||
| <span>New fast filter</span> | ||
| </button> | ||
| </mat-menu> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift
Use Angular built-in control flow in the new mobile menu block
The added mobile section still uses structural directives (*ngIf, *ngFor) in new code (Line 44, Line 58, Line 72). Please migrate these to @if/@for.
As per coding guidelines, "Use Angular 19's built-in control flow (@if, @for, @switch) instead of structural directives (*ngIf, *ngFor, *ngSwitch) in all new code".
🤖 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/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.html`
around lines 44 - 81, Replace the new template usages of structural directives
with Angular 19 control-flow syntax: wrap the menu trigger block that currently
uses *ngIf="savedFilterData.length !== 0" in an <ng-container> using `@if` with
the same condition; change the *ngFor on the menu item to an <ng-container>
using `@for`="let filter of savedFilterData" (keeping the same bindings inside the
generated button), and replace the other *ngIf (canEditConnection()) with `@if` on
an <ng-container> so canEditConnection(), selectFiltersSet(filter.id),
setCurrentFilter(filter), selectedFilterSetId, savedFilterMap and
handleOpenSavedFiltersDialog() continue to be referenced unchanged; ensure you
stopPropagation() where you did before and preserve matMenuTriggerFor bindings
and classes.
| <span *ngIf="canEditConnection()" | ||
| class="saved-filters-menu__row-menu" | ||
| [matMenuTriggerFor]="filterMenu" | ||
| (click)="$event.stopPropagation(); setCurrentFilter(filter)"> | ||
| <mat-icon>more_vert</mat-icon> | ||
| </span> |
There was a problem hiding this comment.
Use a real button for the per-row menu trigger
Line 64-69 uses a clickable <span> as an action target. That’s not reliably keyboard-accessible and weakens semantics for assistive tech. Use a button (e.g., mat-icon-button) for this trigger.
Suggested patch
- <span *ngIf="canEditConnection()"
- class="saved-filters-menu__row-menu"
- [matMenuTriggerFor]="filterMenu"
- (click)="$event.stopPropagation(); setCurrentFilter(filter)">
+ <button *ngIf="canEditConnection()"
+ mat-icon-button
+ type="button"
+ class="saved-filters-menu__row-menu"
+ aria-label="Filter options"
+ [matMenuTriggerFor]="filterMenu"
+ (click)="$event.stopPropagation(); setCurrentFilter(filter)">
<mat-icon>more_vert</mat-icon>
- </span>
+ </button>🤖 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/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.html`
around lines 64 - 69, Replace the clickable <span> used as the per-row menu
trigger with a real <button> (e.g., mat-icon-button) to restore keyboard
accessibility and correct semantics: locate the template block that calls
canEditConnection() and uses [matMenuTriggerFor]="filterMenu" and
(click)="$event.stopPropagation(); setCurrentFilter(filter)" (the
saved-filters-menu__row-menu element) and change it to a button element, keep
the matMenuTriggerFor binding and the click handler (including stopPropagation
and setCurrentFilter(filter)), add type="button" and an appropriate aria-label,
and preserve the mat-icon and CSS class so styling/behavior remain the same.
| <a mat-icon-button class="row-edit-header__back" | ||
| routerLink="/dashboard/{{connectionID}}/{{tableName}}" | ||
| [queryParams]="backUrlParams" | ||
| matTooltip="Back to table" | ||
| data-testid="record-back-to-table-button"> | ||
| <mat-icon>arrow_back</mat-icon> | ||
| </a> |
There was a problem hiding this comment.
Add an accessible name to the icon-only back action.
The control is icon-only; add aria-label to ensure reliable screen-reader output.
Suggested fix
<a mat-icon-button class="row-edit-header__back"
+ aria-label="Back to table"
routerLink="/dashboard/{{connectionID}}/{{tableName}}"
[queryParams]="backUrlParams"
matTooltip="Back to table"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a mat-icon-button class="row-edit-header__back" | |
| routerLink="/dashboard/{{connectionID}}/{{tableName}}" | |
| [queryParams]="backUrlParams" | |
| matTooltip="Back to table" | |
| data-testid="record-back-to-table-button"> | |
| <mat-icon>arrow_back</mat-icon> | |
| </a> | |
| <a mat-icon-button class="row-edit-header__back" | |
| aria-label="Back to table" | |
| routerLink="/dashboard/{{connectionID}}/{{tableName}}" | |
| [queryParams]="backUrlParams" | |
| matTooltip="Back to table" | |
| data-testid="record-back-to-table-button"> | |
| <mat-icon>arrow_back</mat-icon> | |
| </a> |
🤖 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/app/components/db-table-row-edit/db-table-row-edit.component.html`
around lines 24 - 30, The back link element (anchor with class
"row-edit-header__back" and data-testid "record-back-to-table-button") is
icon-only and needs an accessible name; add an aria-label (e.g.,
aria-label="Back to table" or use a localized label) to the anchor so screen
readers announce its purpose, keeping the existing routerLink, [queryParams],
matTooltip and mat-icon intact.
| --mdc-dialog-container-shape: 16px 16px 0 0; | ||
| max-height: 85vh; | ||
| padding-bottom: env(safe-area-inset-bottom, 0px); |
There was a problem hiding this comment.
Add the blank line before max-height to satisfy Stylelint.
Stylelint reports declaration-empty-line-before at Line 119 because a declaration follows a custom-property declaration without a separating blank line. This may fail CI lint.
🎨 Proposed fix
.mobile-bottom-sheet-dialog .mat-mdc-dialog-container {
--mdc-dialog-container-shape: 16px 16px 0 0;
+
max-height: 85vh;
padding-bottom: env(safe-area-inset-bottom, 0px);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| --mdc-dialog-container-shape: 16px 16px 0 0; | |
| max-height: 85vh; | |
| padding-bottom: env(safe-area-inset-bottom, 0px); | |
| --mdc-dialog-container-shape: 16px 16px 0 0; | |
| max-height: 85vh; | |
| padding-bottom: env(safe-area-inset-bottom, 0px); |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 119-119: Expected empty line before declaration (declaration-empty-line-before)
(declaration-empty-line-before)
🤖 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/styles.scss` around lines 118 - 120, Insert a blank line between
the custom-property declaration --mdc-dialog-container-shape and the subsequent
declaration max-height to satisfy Stylelint's declaration-empty-line-before
rule; locate the rule block containing --mdc-dialog-container-shape, max-height,
and padding-bottom and add a single empty line before the max-height declaration
so the sequence is: --mdc-dialog-container-shape, (blank line), max-height,
padding-bottom.
| @keyframes mobileBottomSheetSlideUp { | ||
| from { | ||
| transform: translateY(100%); | ||
| } | ||
| to { | ||
| transform: translateY(0); | ||
| } | ||
| } |
There was a problem hiding this comment.
Rename keyframes to kebab-case and update the reference.
Stylelint reports keyframes-name-pattern at Line 167 — mobileBottomSheetSlideUp should be kebab-case. Remember to also update the animation reference at Line 114 so the animation still resolves.
🎨 Proposed fix
- animation: mobileBottomSheetSlideUp 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
+ animation: mobile-bottom-sheet-slide-up 260ms cubic-bezier(0.2, 0.8, 0.2, 1);-@keyframes mobileBottomSheetSlideUp {
+@keyframes mobile-bottom-sheet-slide-up {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @keyframes mobileBottomSheetSlideUp { | |
| from { | |
| transform: translateY(100%); | |
| } | |
| to { | |
| transform: translateY(0); | |
| } | |
| } | |
| `@keyframes` mobile-bottom-sheet-slide-up { | |
| from { | |
| transform: translateY(100%); | |
| } | |
| to { | |
| transform: translateY(0); | |
| } | |
| } |
🧰 Tools
🪛 Stylelint (17.12.0)
[error] 167-167: Expected keyframe name "mobileBottomSheetSlideUp" to be kebab-case (keyframes-name-pattern)
(keyframes-name-pattern)
🤖 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/styles.scss` around lines 167 - 174, The keyframes identifier
mobileBottomSheetSlideUp must be renamed to kebab-case (e.g.,
mobile-bottom-sheet-slide-up) and every reference to it in the stylesheet
updated accordingly; specifically rename the `@keyframes` declaration name and
update any animation/animation-name/shorthand properties that currently
reference mobileBottomSheetSlideUp so they use the new kebab-case name (ensure
exact string match for shorthand usages).
… inputs Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
End-to-end mobile pass over the data-grid experience.
db-table-view): rows become cards on ≤600px with dashed separators between fields, checkbox + actions strip non-clickable for preview, action icons unified to outlined symbols. Refresh icon replaced with pull-to-refresh; Add row promoted to an extended FAB; AI and Settings gear moved onto the table-switcher row; sort arrow appended to the active column's data-label.position: fixedpins to the viewport inmat-sidenav-content(no transformed ancestor), so the long button reflows onto its own row underneath.<picture>.verticalPositionso the preview's copy-link toast shows above the sheet.Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements