Mobile fixes update#1829
Conversation
… polish - Permissions (Cedar policy) dialog opens as a mobile bottom sheet via panelClass - Empty state in the policy list gets an inline "Add policy" CTA (accent color), and the dialog actions now keep only one "Add policy" once there's at least one policy - Cancel pinned to the left of the dialog footer; Save stays on the right - User add dialog: swap order so the primary "Add" sits before "Cancel" - Users page on mobile: wrapper full-width with 16px padding, header flex-wraps the New group button if needed, smaller h1 - Connection settings on mobile: keep Primary/Accented color inputs on one row by forcing min-width: 0 through the Material form-field internals Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Audit mobile feed: replace mat-table with a card list (sticky date headers grouping entries per day, avatar with initials, top row user/time, middle row action+table, bottom row status+Details, last card fades for an infinite-scroll hint, tap anywhere on the card opens the details dialog) - Audit header on mobile: arrow-back next to the "Audit" title, Tables/Users selects side-by-side without breaking layout (force min-width: 0 chain, no horizontal overflow), select arrow pinned to the right edge of the field - Audit paginator: match the table paginator (compact "Per page:" select on the left, range + nav buttons on the right, single row, capitalized label) - AI panel: raise z-index of the expanded content back to 100 so close/send buttons aren't covered (they were being intercepted by other UI when the panel is force-expanded) - Sidebar drawer: close automatically on any nav-list click and on Upgrade click Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Widgets page mobile layout: stacked widget cards (display:contents → block), header with arrow-back + "Widgets" / table-name stack, hide breadcrumbs and per-card delete button, drop the bottom "Back" button (arrow-back covers it), Save aligns right - Widgets empty state mobile: hide right-side widget cards grid, hide divider and the tune icon, symmetric padding, Docs link + "Create first widget" button right-aligned together, breadcrumbs hidden, extra bottom buffer so Create doesn't collide with the version footer - AI panel: full-size only on mobile (removed expand/collapse toggle and the +175px expanded padding), trim "AI insights for <table>" to plain "AI insights" so the title doesn't push out, FAB Add row hides while the chat is open, and on mobile the chat panel state is reset on each table view init (so navigating away doesn't reopen the chat on return) - Sidebar drawer mobile: sits below the 44px nav-bar (with backdrop offset), hide the dark mat-toolbar, "Connections" hidden when already inside a connection, "Hosted databases" added between Company and Secrets, account-section items (.account-section-item) get smaller text + icons, drop the connection-nav top border, Upgrade button nudged up - Connections list dropdown: divider + "Hosted databases" link at the bottom - Logo: short variant only on mobile when inside a connection (connections-list, account, hosted-databases pages keep the full wordmark) - Row edit form: drop the leftover .widget grid column reservation so inputs fill width - Mobile loading skeleton: dedicated card-shape placeholder for ≤600px, no left label column, no elevation around the wrapper - Row preview dividers: inset 20px each side via ::after so they don't reach the popup edges - Filter dialog when tapping an active filter chip: opens as the mobile bottom sheet (panelClass: 'mobile-bottom-sheet-dialog') Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nator polish - Add a "+" / "Create fast filter" trigger after the Filter button on mobile, with a vertical divider in between - Horizontally scroll the saved-filter chips inside the search row (custom chip render, absolutely positioned strip so it can't push the page wide) - Each chip has a three-dots menu (Edit / Delete) wired to the saved-filters-panel handlers via @ViewChild - Hide the panel's own mobile dropdown/list — its conditions editor remains visible below the row when a fast filter is selected - When conditions show, the panel takes the full second row (:has) and Columns/Sort stay right-aligned on the row above - Tighten spacing between conditions and the Columns/Sort row; align the "where" label and chips vertically (min-height: 32px) - Make Sort by default black/white, accent only when sort is active; pull the swap_vert icon tight to the label - Inline preview divider only spans the text area (::after with 20px insets) instead of edge-to-edge - Paginator on mobile: items-per-page flush left, range/buttons right, single row, "Rows per page:" label, looser select for readability - Drop the trailing period from the "No records match…" empty-state strings - Open the filter dialog as a mobile bottom sheet when the active filter chip is tapped (was a centered modal) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughMobile-responsive frontend updates: sidebar/account navigation changes, mobile audit card feed, table-view quick filters and mobile chips, AI panel expansion/styling, widgets mobile header/cards, users/cedar-policy UI signal migration and dialog mobile styling, plus broad responsive CSS and skeleton updates. ChangesMobile-Responsive Dashboard Redesign
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (4 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.
Pull request overview
This PR focuses on improving the mobile experience across several key frontend screens (table view, saved/fast filters, audit logs, widgets, users/groups, dialogs, and navigation), primarily through responsive layout changes and mobile-specific UI patterns (bottom-sheet dialogs, chip rows, card layouts).
Changes:
- Adds mobile-specific layouts and styling for Audit, Widgets, Users/Groups, skeleton placeholders, and various settings panels.
- Introduces a mobile “fast filters” chip row in the table view and refines saved-filters UI behavior on small screens.
- Updates dialog presentation for mobile (bottom-sheet class) and tweaks global navigation/menu behavior (including a Hosted databases entry).
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
frontend/src/styles.scss |
Hides the mobile “Add row” FAB when the AI panel is open. |
frontend/src/app/components/users/users.component.ts |
Applies the mobile bottom-sheet dialog class to the Cedar policy editor dialog. |
frontend/src/app/components/users/users.component.html |
Reworks group member rendering from Material list to plain <ul>/<li> structure. |
frontend/src/app/components/users/users.component.css |
Adds member row typography/layout styles and mobile header/wrapper tweaks. |
frontend/src/app/components/users/user-add-dialog/user-add-dialog.component.html |
Reorders dialog actions (moves Cancel button to the end). |
frontend/src/app/components/users/cedar-policy-list/cedar-policy-list.component.html |
Improves empty state with paragraph + “Add policy” CTA button. |
frontend/src/app/components/users/cedar-policy-list/cedar-policy-list.component.css |
Updates empty-state layout to support the new CTA. |
frontend/src/app/components/users/cedar-policy-editor-dialog/cedar-policy-editor-dialog.component.html |
Reorders dialog actions and conditions for showing “Add policy”. |
frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.ts |
Adds a second skeleton variant count for mobile “card” placeholders. |
frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.html |
Adds a mobile card-based skeleton layout alongside the desktop table skeleton. |
frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css |
Adds responsive styles to switch skeleton layouts on mobile. |
frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.css |
Changes .widget layout from grid to block. |
frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.css |
Adjusts mobile saved-filters UI layout/visibility and filter-row spacing. |
frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.css |
Adds a mobile layout for the widgets empty state. |
frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.html |
Adds a dedicated mobile header and tweaks Back button placement/classes. |
frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.css |
Adds extensive mobile layout rules for widgets page and cards. |
frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts |
Adds quick-filter dialog opener wiring, forces AI panel closed on mobile entry, and customizes paginator label. |
frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html |
Adds mobile quick-filter chip UI and minor copy tweak for empty-filter messaging. |
frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css |
Adds mobile styling for quick-filter chips and paginator layout refinements. |
frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.css |
Changes row preview separators to use a pseudo-element divider. |
frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts |
Forces AI panel to default to expanded and removes collapse behavior. |
frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.html |
Simplifies AI panel header and removes expand/collapse button. |
frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css |
Adjusts AI panel expanded layout and stacking context. |
frontend/src/app/components/connection-settings/connection-settings.component.css |
Adds mobile layout fixes for color theme controls. |
frontend/src/app/components/audit/audit.component.ts |
Adds mobile feed grouping helpers and custom paginator label. |
frontend/src/app/components/audit/audit.component.html |
Adds a mobile “feed” view (grouped cards) with a back button. |
frontend/src/app/components/audit/audit.component.css |
Adds extensive mobile styling for the audit feed/cards and header controls. |
frontend/src/app/app.component.ts |
Imports MatDividerModule to support new divider usage. |
frontend/src/app/app.component.html |
Closes sidenav on nav click, adds Hosted databases navigation entries, and tweaks logo source logic. |
frontend/src/app/app.component.css |
Adjusts menu item sizing and mobile sidenav positioning/layout. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @if (currentUser()?.email !== user.email && canManage()) { | ||
| <button matListItemMeta type="button" mat-icon-button | ||
| angulartics2On="click" |
| .user { | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| padding: 8px 0; | ||
| } |
| <div *ngFor="let f of savedFiltersPanel?.savedFilterData" | ||
| class="search-row__quick-chip" | ||
| [class.search-row__quick-chip_active]="savedFiltersPanel?.selectedFilterSetId === f.id" | ||
| (click)="savedFiltersPanel?.selectFiltersSet(f.id)"> | ||
| <span class="search-row__quick-chip-label">{{ f.name }}</span> |
| <div *ngFor="let entry of group.entries; let last = last" | ||
| class="audit-card" | ||
| [class.audit-card_fade]="last && mobileGroupedLogs.length > 0" | ||
| (click)="openInfoLogDialog(entry); posthog.capture('Audit: view changes is clicked')"> |
| this.dataSource = new AuditDataSource(this._connections); | ||
| this.dataSource.connect(null as any).subscribe((rows) => { | ||
| this.mobileGroupedLogs = this.groupByDate(rows); | ||
| }); |
| private route: ActivatedRoute, | ||
| public router: Router, | ||
| public dialog: MatDialog, | ||
| private cdr: ChangeDetectorRef, | ||
| ) {} | ||
| private paginatorIntl: MatPaginatorIntl, | ||
| ) { | ||
| this.paginatorIntl.itemsPerPageLabel = 'Rows per page:'; | ||
| this.paginatorIntl.changes.next(); | ||
| } |
| @@ -78,7 +84,11 @@ export class AuditComponent implements OnInit { | |||
| private _companyService: CompanyService, | |||
| public dialog: MatDialog, | |||
| private title: Title, | |||
| ) {} | |||
| private paginatorIntl: MatPaginatorIntl, | |||
| ) { | |||
| this.paginatorIntl.itemsPerPageLabel = 'Per page:'; | |||
| this.paginatorIntl.changes.next(); | |||
| } | |||
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (3)
frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts (1)
184-184: ⚡ Quick winAvoid
anytype—use specific type forchipFilterForMenu.The
chipFilterForMenuproperty is explicitly typed asany, which violates the TypeScript coding guideline to use specific types instead ofany.♻️ Suggested type improvement
Based on the template usage and context snippets showing the saved filter data structure, this should be typed to match the saved filter object:
- public chipFilterForMenu: any = null; + public chipFilterForMenu: { + id: string; + name: string; + filters: any; + filterEntries?: { column: string; operator: string; value: string }[]; + staticFilters?: { column: string; operator: string; value: any }[]; + dynamicColumn?: { column: string; operator: string; value: any } | null; + } | null = null;Or create a dedicated interface for the saved filter type and reference it here.
As per coding guidelines: "Avoid any types - use specific types or generics instead" for
**/*.{ts,tsx}.🤖 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.ts` at line 184, The property chipFilterForMenu is typed as any—replace it with a specific interface/type that matches the saved filter shape used by the template (e.g., create a SavedFilter or ChipFilter type describing fields like id, name, criteria, metadata, etc.) and change the declaration in the DbTableViewComponent from public chipFilterForMenu: any to public chipFilterForMenu: SavedFilter | null (or an appropriate union/array), updating any usages to conform to the new type; ensure the new interface is exported/declared near the component so other code can reuse it.frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts (1)
100-102: 💤 Low valueThe subscription ignores the emitted value—consider refactoring.
The subscription to
aiPanelExpandedCastignores the observable's emitted value and unconditionally setsisExpanded = true. This pattern suggests the observable is being used as a trigger rather than a state stream. If the emitted value is no longer meaningful, consider renaming the observable (e.g.,aiPanelExpandTrigger) or updating the service to emitvoidto clarify intent.♻️ Suggested clarification
Option 1: If the observable should control state, use the emitted value:
- this._tableState.aiPanelExpandedCast.subscribe(() => { - this.isExpanded = true; + this._tableState.aiPanelExpandedCast.subscribe((isExpanded) => { + this.isExpanded = isExpanded; });Option 2: If it's just a trigger to expand, clarify by naming:
- this._tableState.aiPanelExpandedCast.subscribe(() => { + // Observable is used as an expand trigger, not state + this._tableState.aiPanelExpandedCast.subscribe(() => { this.isExpanded = true; });🤖 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-ai-panel/db-table-ai-panel.component.ts` around lines 100 - 102, The subscription to _tableState.aiPanelExpandedCast currently ignores the emitted value and always sets isExpanded = true; change this to either (A) use the emitted boolean by subscribing with value => this.isExpanded = value so aiPanelExpandedCast becomes a state stream, or (B) if it is only a trigger, rename the observable in the TableState service to aiPanelExpandTrigger (or change its type to Observable<void>) and keep the subscription as a trigger handler (e.g., this._tableState.aiPanelExpandTrigger.subscribe(() => this.isExpanded = true)); update references to _tableState.aiPanelExpandedCast and the service definition accordingly.frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css (1)
78-78: 💤 Low valueRedundant z-index declaration.
Line 56 already sets
z-index: 100on.ai-panel-sidebar-content, so this declaration on line 78 is redundant. Consider removing it to reduce duplication.♻️ Proposed cleanup
.ai-panel-sidebar-content_open.ai-panel-sidebar-content_expanded { left: 65px; width: calc(100vw - 65px); padding-left: 0; border-left: none; - z-index: 100; transition: left 400ms cubic-bezier(0.4, 0, 0.2, 1), width 400ms cubic-bezier(0.4, 0, 0.2, 1),🤖 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-ai-panel/db-table-ai-panel.component.css` at line 78, Remove the redundant z-index declaration by deleting the duplicate "z-index: 100" in the later CSS rule so that only the existing declaration on .ai-panel-sidebar-content remains; locate the repeated property in db-table-ai-panel.component.css (the second occurrence) and remove it to avoid duplication while keeping the original .ai-panel-sidebar-content z-index intact.
🤖 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/app.component.html`:
- Line 105: Replace the deprecated structural directive used on the <source>
element (the *ngIf that checks connectionID) with Angular 19's control-flow
syntax: remove the *ngIf and add an equivalent `@if` control that evaluates
connectionID, preserving the element's media and srcset attributes and behavior;
locate the <source> element in app.component.html and apply the change so the
element renders only when connectionID is truthy using the new `@if` syntax.
In `@frontend/src/app/components/audit/audit.component.html`:
- Around line 80-84: The clickable audit card div is not keyboard-accessible;
replace the <div class="audit-card" ... (click)="openInfoLogDialog(entry);
posthog.capture('Audit: view changes is clicked')"> with a semantic interactive
element (preferably <button type="button">) preserving the class and bindings,
or if you must keep the div add tabindex="0", role="button" and keydown handlers
for Enter/Space that call the same openInfoLogDialog(entry) and
posthog.capture(...) logic; ensure the [class.audit-card_fade] binding and inner
content ({{ getInitials(entry.UserEmail) }}) remain unchanged and apply the same
change for the other occurrence referenced (line ~104).
In `@frontend/src/app/components/audit/audit.component.ts`:
- Around line 113-115: The two long-lived subscriptions in AuditComponent (the
dataSource.connect(...) subscription in ngOnInit and the
merge(this.paginator.page...).subscribe(...) in ngAfterViewInit) need automatic
teardown to avoid leaks; update both subscriptions to pipe through a destruction
operator—preferably use takeUntilDestroyed(inject(DestroyRef)) or add a private
destroy$ Subject and pipe(takeUntil(this.destroy$)) and complete it in
ngOnDestroy—so modify the subscribe calls on dataSource.connect and the
merge(...) stream to include the takeUntil logic and ensure destroy cleanup is
implemented.
- Around line 72-73: Replace the untyped any[] usage by introducing a concrete
row interface (e.g., AuditRow with DateOnly, UserEmail, TimeOnly, ActionIcon,
Action, Table, Status, etc.) and a group interface (e.g., MobileGroup { date:
string; entries: AuditRow[] }), then change mobileGroupedLogs to MobileGroup[]
and update the groupByDate function signature and implementation to
return/consume AuditRow instead of any; additionally update AuditDataSource to
use DataSource<AuditRow> and BehaviorSubject<AuditRow[]> and change the
subscribe((res: any) => ...) to subscribe((res: AuditRow[]) => ...) and any code
that constructs rows to build AuditRow instances so typing is preserved
end-to-end (references: mobileGroupedLogs, groupByDate, AuditDataSource,
DataSource, BehaviorSubject, rows, subscribe).
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts`:
- Line 199: The MatPaginatorIntl dependency is currently injected via the
constructor parameter (paginatorIntl) but should use Angular's inject()
function; update the db-table-view.component to declare a private field (e.g.,
private _paginatorIntl = inject(MatPaginatorIntl)) and remove the
MatPaginatorIntl parameter from the constructor, then update any usages that
referenced paginatorIntl to use the new _paginatorIntl field (ensure imports
remain and adjust accessibility if needed).
- Around line 305-308: DbTableAiPanelComponent's aiPanelExpandedCast
subscription ignores emitted values and always sets this.isExpanded = true, so
collapse events from TableStateService (aiPanelSubject) and
toggleAIPanelExpanded()/toggleExpand() are not respected; update the
subscription in DbTableAiPanelComponent to set this.isExpanded = emittedValue
(or its boolean negation where intended) instead of forcing true, ensuring it
reads the incoming value from aiPanelExpandedCast and updates the panel
expansion state accordingly; verify TableStateService.closeAIpanel() and
aiPanelCast/isAIpanelOpened logic remain unchanged so the component simply
mirrors the emitted expansion boolean.
In
`@frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css`:
- Around line 39-47: The .mobile-card rule in
placeholder-table-data.component.css uses a hardcoded fallback "`#fff`" for
--mat-table-background-color; replace that hardcoded value with a generated
palette CSS variable from the theme (the palette variables defined in
colorConfig/@brumeilde/ngx-theme) so the fallback becomes a palette reference
(e.g., use var(--mat-table-background-color, <palette-var>)); update the
.mobile-card declaration in placeholder-table-data.component.css to reference
the appropriate palette variable instead of "`#fff`".
In
`@frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.html`:
- Around line 1-14: Replace the Angular structural directives *ngFor with
Angular 19 built-in control flow `@for` for both iterations over numberOfDivs and
numberOfCards: locate the two occurrences using the variables numberOfDivs and
numberOfCards and convert the templates to use `@for` (the new for-loop syntax) so
the repeated .table-cell and .mobile-card elements are produced by `@for` instead
of *ngFor; keep the same loop index (i) and class structure and ensure
bindings/loop variables are updated to the `@for` syntax.
In `@frontend/src/app/components/users/users.component.css`:
- Around line 127-136: The .user__email rule uses hardcoded rgba colors; update
both the default and `@media` (prefers-color-scheme: dark) selectors to use the
generated palette CSS variables from `@brumeilde/ngx-theme` (as defined by
colorConfig in src/main.ts) instead of rgba values — replace the light-mode
color with the appropriate palette var (e.g., a neutral/text-muted variable) and
the dark-mode color with the corresponding dark-theme palette var so both
.user__email declarations reference palette CSS variables rather than hardcoded
rgba().
In `@frontend/src/app/components/users/users.component.html`:
- Around line 113-119: Remove the obsolete matListItemMeta attribute from the
delete button in the users template: locate the <button> that calls
openDeleteUserDialog(user, groupItem.group) and posthog.capture(...) and delete
the matListItemMeta directive (leave mat-icon-button, angulartics2On,
angularticsAction, matTooltip and the (click) handler intact) so the button is
plain HTML inside the <li> after the refactor from mat-list to ul/li.
---
Nitpick comments:
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css`:
- Line 78: Remove the redundant z-index declaration by deleting the duplicate
"z-index: 100" in the later CSS rule so that only the existing declaration on
.ai-panel-sidebar-content remains; locate the repeated property in
db-table-ai-panel.component.css (the second occurrence) and remove it to avoid
duplication while keeping the original .ai-panel-sidebar-content z-index intact.
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts`:
- Around line 100-102: The subscription to _tableState.aiPanelExpandedCast
currently ignores the emitted value and always sets isExpanded = true; change
this to either (A) use the emitted boolean by subscribing with value =>
this.isExpanded = value so aiPanelExpandedCast becomes a state stream, or (B) if
it is only a trigger, rename the observable in the TableState service to
aiPanelExpandTrigger (or change its type to Observable<void>) and keep the
subscription as a trigger handler (e.g.,
this._tableState.aiPanelExpandTrigger.subscribe(() => this.isExpanded = true));
update references to _tableState.aiPanelExpandedCast and the service definition
accordingly.
In
`@frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts`:
- Line 184: The property chipFilterForMenu is typed as any—replace it with a
specific interface/type that matches the saved filter shape used by the template
(e.g., create a SavedFilter or ChipFilter type describing fields like id, name,
criteria, metadata, etc.) and change the declaration in the DbTableViewComponent
from public chipFilterForMenu: any to public chipFilterForMenu: SavedFilter |
null (or an appropriate union/array), updating any usages to conform to the new
type; ensure the new interface is exported/declared near the component so other
code can reuse it.
🪄 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: 50fe63e6-3a35-493f-b548-24459e08666b
📒 Files selected for processing (30)
frontend/src/app/app.component.cssfrontend/src/app/app.component.htmlfrontend/src/app/app.component.tsfrontend/src/app/components/audit/audit.component.cssfrontend/src/app/components/audit/audit.component.htmlfrontend/src/app/components/audit/audit.component.tsfrontend/src/app/components/connection-settings/connection-settings.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlfrontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.tsfrontend/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-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/db-table-widgets/db-table-widgets.component.cssfrontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.htmlfrontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.cssfrontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-panel.component.cssfrontend/src/app/components/db-table-row-edit/db-table-row-edit.component.cssfrontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.cssfrontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.htmlfrontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.tsfrontend/src/app/components/users/cedar-policy-editor-dialog/cedar-policy-editor-dialog.component.htmlfrontend/src/app/components/users/cedar-policy-list/cedar-policy-list.component.cssfrontend/src/app/components/users/cedar-policy-list/cedar-policy-list.component.htmlfrontend/src/app/components/users/user-add-dialog/user-add-dialog.component.htmlfrontend/src/app/components/users/users.component.cssfrontend/src/app/components/users/users.component.htmlfrontend/src/app/components/users/users.component.tsfrontend/src/styles.scss
| <ng-template #defaultRocketLogo> | ||
| <picture> | ||
| <source media="(max-width: 600px)" srcset="../assets/rocketadmin_logo_white-short.svg"> | ||
| <source *ngIf="connectionID" media="(max-width: 600px)" srcset="../assets/rocketadmin_logo_white-short.svg"> |
There was a problem hiding this comment.
Use Angular 19 control flow syntax for new code.
This new <source> element uses the deprecated *ngIf structural directive. As per coding guidelines, all new code in Angular 19 should use the built-in control flow syntax (@if) instead.
♻️ Recommended refactor to use `@if` syntax
<ng-template `#defaultRocketLogo`>
<picture>
- <source *ngIf="connectionID" media="(max-width: 600px)" srcset="../assets/rocketadmin_logo_white-short.svg">
+ `@if` (connectionID) {
+ <source media="(max-width: 600px)" srcset="../assets/rocketadmin_logo_white-short.svg">
+ }
<img src="../assets/rocketadmin_logo_white.svg" alt="Rocketadmin logo" class="logo__image">
</picture>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/app.component.html` at line 105, Replace the deprecated
structural directive used on the <source> element (the *ngIf that checks
connectionID) with Angular 19's control-flow syntax: remove the *ngIf and add an
equivalent `@if` control that evaluates connectionID, preserving the element's
media and srcset attributes and behavior; locate the <source> element in
app.component.html and apply the change so the element renders only when
connectionID is truthy using the new `@if` syntax.
| <div *ngFor="let entry of group.entries; let last = last" | ||
| class="audit-card" | ||
| [class.audit-card_fade]="last && mobileGroupedLogs.length > 0" | ||
| (click)="openInfoLogDialog(entry); posthog.capture('Audit: view changes is clicked')"> | ||
| <div class="audit-card__avatar">{{ getInitials(entry.UserEmail) }}</div> |
There was a problem hiding this comment.
Make the mobile audit card action keyboard-accessible.
Line 80 uses a clickable div (Line 83) for opening details; this control is not keyboard-focusable by default. Use a semantic button/a (or add full keyboard semantics) so users can trigger “Details” without a pointer.
Also applies to: 104-104
🤖 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/audit/audit.component.html` around lines 80 - 84,
The clickable audit card div is not keyboard-accessible; replace the <div
class="audit-card" ... (click)="openInfoLogDialog(entry);
posthog.capture('Audit: view changes is clicked')"> with a semantic interactive
element (preferably <button type="button">) preserving the class and bindings,
or if you must keep the div add tabindex="0", role="button" and keydown handlers
for Enter/Space that call the same openInfoLogDialog(entry) and
posthog.capture(...) logic; ensure the [class.audit-card_fade] binding and inner
content ({{ getInitials(entry.UserEmail) }}) remain unchanged and apply the same
change for the other occurrence referenced (line ~104).
| public mobileGroupedLogs: { date: string; entries: any[] }[] = []; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and display the relevant sections in the component and datasource.
FILES=(
"frontend/src/app/components/audit/audit.component.ts"
"frontend/src/app/components/audit/audit-data-source.ts"
)
for f in "${FILES[@]}"; do
echo "===== $f (head/tail for context: line count) ====="
if [ -f "$f" ]; then
wc -l "$f"
else
echo "MISSING: $f" >&2
fi
echo "===== $f (lines 50-120) ====="
if [ -f "$f" ]; then
nl -ba "$f" | sed -n '50,130p'
fi
echo "===== $f (lines 160-240) ====="
if [ -f "$f" ]; then
nl -ba "$f" | sed -n '160,240p'
fi
echo
done
# Find any occurrences of "any" in the audit component (and datasource for context).
echo "===== 'any' occurrences in audit.component.ts ====="
if [ -f "frontend/src/app/components/audit/audit.component.ts" ]; then
rg -n "\bany\b" "frontend/src/app/components/audit/audit.component.ts" || true
fi
echo "===== 'any' occurrences in audit-data-source.ts ====="
if [ -f "frontend/src/app/components/audit/audit-data-source.ts" ]; then
rg -n "\bany\b" "frontend/src/app/components/audit/audit-data-source.ts" || true
fi
# Show the row contract / types referenced from audit-data-source.ts (best-effort).
echo "===== audit-data-source.ts: exported types/interfaces containing DateOnly/UserEmail ====="
if [ -f "frontend/src/app/components/audit/audit-data-source.ts" ]; then
rg -n "DateOnly|UserEmail|TimeOnly|Status" "frontend/src/app/components/audit/audit-data-source.ts" || true
fiRepository: rocket-admin/rocketadmin
Length of output: 368
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILES=(
"frontend/src/app/components/audit/audit.component.ts"
"frontend/src/app/components/audit/audit-data-source.ts"
)
show_range () {
local f="$1"
local start="$2"
local end="$3"
if [ -f "$f" ]; then
echo "===== $f (lines ${start}-${end}) ====="
awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' "$f"
else
echo "===== $f (missing) =====" >&2
fi
}
for f in "${FILES[@]}"; do
if [ -f "$f" ]; then
echo "===== $f (line count) ====="
wc -l "$f"
else
echo "MISSING: $f" >&2
fi
done
show_range "frontend/src/app/components/audit/audit.component.ts" 60 90
show_range "frontend/src/app/components/audit/audit.component.ts" 160 210
show_range "frontend/src/app/components/audit/audit.component.ts" 210 260
show_range "frontend/src/app/components/audit/audit-data-source.ts" 1 220
echo "===== 'any' occurrences in audit.component.ts ====="
rg -n "\bany\b" "frontend/src/app/components/audit/audit.component.ts" || true
echo "===== 'any' occurrences in audit-data-source.ts ====="
rg -n "\bany\b" "frontend/src/app/components/audit/audit-data-source.ts" || trueRepository: rocket-admin/rocketadmin
Length of output: 6703
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search usages in the component for mobileGroupedLogs/groupByDate
rg -n "mobileGroupedLogs|groupByDate" frontend/src/app/components/audit/audit.component.ts
# Print around the groupByDate call site(s) and any template-related assignments/usages
# (Try several ranges to capture the relevant blocks without dumping the whole file.)
awk 'NR>=90 && NR<=170 {print NR"\t"$0}' frontend/src/app/components/audit/audit.component.ts
awk 'NR>=170 && NR<=197 {print NR"\t"$0}' frontend/src/app/components/audit/audit.component.ts
# Show the template file if it exists (Angular component template is often .html)
# Find any html file next to this component.
ls -1 frontend/src/app/components/audit || true
find frontend/src/app/components/audit -maxdepth 1 -type f -printRepository: rocket-admin/rocketadmin
Length of output: 4043
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show relevant template sections for mobileGroupedLogs/entries usage
rg -n "mobileGroupedLogs|groupByDate|trackByDate|entries\.|DateOnly" frontend/src/app/components/audit/audit.component.html
# Print a useful window around the first match(es)
# (Use line numbers from ripgrep output by just dumping ranges covering typical rendering blocks.)
awk 'NR>=1 && NR<=260 {print NR"\t"$0}' frontend/src/app/components/audit/audit.component.html | sed -n '1,260p'Repository: rocket-admin/rocketadmin
Length of output: 10373
Replace any-typed mobile audit grouping with a typed row/group model
frontend/src/app/components/audit/audit.component.ts uses any[] for mobileGroupedLogs (line 72) and in groupByDate (lines 188–196). The mobile template directly reads fields like DateOnly, UserEmail, TimeOnly, ActionIcon, Action, Table, and Status, so any removes compile-time guarantees for the render path.
Proposed typed fix
+interface AuditFeedRow {
+ DateOnly: string;
+ UserEmail: string;
+ TimeOnly: string;
+ Action?: string;
+ ActionIcon?: string;
+ Table?: string;
+ Status?: string;
+}
+
+interface MobileGroupedLog {
+ date: string;
+ entries: AuditFeedRow[];
+}
+
-public mobileGroupedLogs: { date: string; entries: any[] }[] = [];
+public mobileGroupedLogs: MobileGroupedLog[] = [];
...
-private groupByDate(rows: any[]): { date: string; entries: any[] }[] {
- const groups = new Map<string, any[]>();
+private groupByDate(rows: AuditFeedRow[]): MobileGroupedLog[] {
+ const groups = new Map<string, AuditFeedRow[]>();This needs pairing with AuditDataSource: it currently emits Object[] (DataSource<Object> + BehaviorSubject<Object[]>) and builds rows from subscribe((res: any) => ...), so the component will otherwise still treat rows as Object[] and lose the end-to-end typing.
🤖 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/audit/audit.component.ts` around lines 72 - 73,
Replace the untyped any[] usage by introducing a concrete row interface (e.g.,
AuditRow with DateOnly, UserEmail, TimeOnly, ActionIcon, Action, Table, Status,
etc.) and a group interface (e.g., MobileGroup { date: string; entries:
AuditRow[] }), then change mobileGroupedLogs to MobileGroup[] and update the
groupByDate function signature and implementation to return/consume AuditRow
instead of any; additionally update AuditDataSource to use DataSource<AuditRow>
and BehaviorSubject<AuditRow[]> and change the subscribe((res: any) => ...) to
subscribe((res: AuditRow[]) => ...) and any code that constructs rows to build
AuditRow instances so typing is preserved end-to-end (references:
mobileGroupedLogs, groupByDate, AuditDataSource, DataSource, BehaviorSubject,
rows, subscribe).
| this.dataSource.connect(null as any).subscribe((rows) => { | ||
| this.mobileGroupedLogs = this.groupByDate(rows); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and inspect the relevant component file
FILE="frontend/src/app/components/audit/audit.component.ts"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE"
exit 1
fi
echo "== Lines around 90-150 =="
nl -ba "$FILE" | sed -n '80,160p'
echo "== Find component lifecycle hooks / teardown =="
rg -n "ngOnDestroy|OnDestroy|takeUntilDestroyed|takeUntil|DestroyRef|Subject|unsubscribe" "$FILE" || true
echo "== Find dataSource definition =="
rg -n "dataSource" "$FILE"
echo "== Show type/initialization of dataSource (near declaration) =="
# Try to show around the first occurrence after the class starts
START_LINE=$(rg -n "class AuditComponent" "$FILE" | head -n1 | cut -d: -f1)
if [ -n "$START_LINE" ]; then
nl -ba "$FILE" | sed -n "$START_LINE, $((START_LINE+200))p"
fiRepository: rocket-admin/rocketadmin
Length of output: 156
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="frontend/src/app/components/audit/audit.component.ts"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE"
exit 1
fi
echo "== Lines around 90-160 =="
awk 'NR>=90 && NR<=160 {printf "%d:%s\n", NR, $0}' "$FILE"
echo "== Find component lifecycle hooks / teardown patterns =="
rg -n "ngOnDestroy|OnDestroy|takeUntilDestroyed|takeUntil\(|DestroyRef|unsubscribe\(|Subject|BehaviorSubject" "$FILE" || true
echo "== Find dataSource connect/subscribe usage =="
rg -n "dataSource\.connect|dataSource" "$FILE"
echo "== Show around class definition (first 220 lines or until EOF) =="
START_LINE=$(rg -n "class AuditComponent" "$FILE" | head -n1 | cut -d: -f1 || true)
if [ -z "${START_LINE:-}" ]; then
echo "class AuditComponent not found"
exit 0
fi
END_LINE=$((START_LINE+220))
awk -v s="$START_LINE" -v e="$END_LINE" 'NR>=s && NR<=e {printf "%d:%s\n", NR, $0}' "$FILE"Repository: rocket-admin/rocketadmin
Length of output: 7264
Add teardown for long-lived RxJS subscriptions in AuditComponent.
ngOnInit:this.dataSource.connect(null as any).subscribe(...)(lines 113-115) has no unsubscribe, risking leaked subscriptions/duplicate updates when the component is recreated.ngAfterViewInit:merge(this.paginator.page)...subscribe()(lines 93-99) also has no teardown.
Use takeUntilDestroyed(inject(DestroyRef)) (or an equivalent takeUntil + destroy notifier) for both subscriptions.
🤖 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/audit/audit.component.ts` around lines 113 - 115,
The two long-lived subscriptions in AuditComponent (the dataSource.connect(...)
subscription in ngOnInit and the merge(this.paginator.page...).subscribe(...) in
ngAfterViewInit) need automatic teardown to avoid leaks; update both
subscriptions to pipe through a destruction operator—preferably use
takeUntilDestroyed(inject(DestroyRef)) or add a private destroy$ Subject and
pipe(takeUntil(this.destroy$)) and complete it in ngOnDestroy—so modify the
subscribe calls on dataSource.connect and the merge(...) stream to include the
takeUntil logic and ensure destroy cleanup is implemented.
| public dialog: MatDialog, | ||
| private cdr: ChangeDetectorRef, | ||
| ) {} | ||
| private paginatorIntl: MatPaginatorIntl, |
There was a problem hiding this comment.
Use inject() function for dependency injection.
The MatPaginatorIntl service is injected via a constructor parameter, but the coding guidelines require using the inject() function for all new dependency injections in Angular 19 frontend code.
♻️ Refactor to use inject() function
+ private _paginatorIntl = inject(MatPaginatorIntl);
+
constructor(
private _tableState: TableStateService,
private _notifications: NotificationsService,
private _tableRow: TableRowService,
private _connections: ConnectionsService,
private _tables: TablesService,
private route: ActivatedRoute,
public router: Router,
public dialog: MatDialog,
private cdr: ChangeDetectorRef,
- private paginatorIntl: MatPaginatorIntl,
) {
- this.paginatorIntl.itemsPerPageLabel = 'Rows per page:';
- this.paginatorIntl.changes.next();
+ this._paginatorIntl.itemsPerPageLabel = 'Rows per page:';
+ this._paginatorIntl.changes.next();
}As per coding guidelines: "Inject dependencies using inject() function (e.g., private _dataService = inject(DataService)) instead of constructor parameters" for frontend/**/{components,services}/**/*.ts.
📝 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.
| private paginatorIntl: MatPaginatorIntl, | |
| private _paginatorIntl = inject(MatPaginatorIntl); | |
| constructor( | |
| private _tableState: TableStateService, | |
| private _notifications: NotificationsService, | |
| private _tableRow: TableRowService, | |
| private _connections: ConnectionsService, | |
| private _tables: TablesService, | |
| private route: ActivatedRoute, | |
| public router: Router, | |
| public dialog: MatDialog, | |
| private cdr: ChangeDetectorRef, | |
| ) { | |
| this._paginatorIntl.itemsPerPageLabel = 'Rows per page:'; | |
| this._paginatorIntl.changes.next(); | |
| } |
🤖 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.ts`
at line 199, The MatPaginatorIntl dependency is currently injected via the
constructor parameter (paginatorIntl) but should use Angular's inject()
function; update the db-table-view.component to declare a private field (e.g.,
private _paginatorIntl = inject(MatPaginatorIntl)) and remove the
MatPaginatorIntl parameter from the constructor, then update any usages that
referenced paginatorIntl to use the new _paginatorIntl field (ensure imports
remain and adjust accessibility if needed).
| // On mobile, never restore a previously open AI panel — user should land on the table, not the chat. | ||
| if (this.isMobileView) { | ||
| this._tableState.closeAIpanel(); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify the interaction between table view mobile init and AI panel expansion state
# Check how TableStateService manages aiPanelExpandedCast emissions
rg -n -A5 -B5 'aiPanelExpandedCast|closeAIpanel' --type=ts -g 'table-state.service.ts'
# Check if there are other call sites that rely on the AI panel's initial isExpanded value
ast-grep --pattern 'isExpanded'Repository: rocket-admin/rocketadmin
Length of output: 2618
🏁 Script executed:
#!/bin/bash
# 1) Find where aiPanelExpandedCast is consumed/emitted
rg -n --type=ts 'aiPanelExpandedCast' frontend/src/app -S
# 2) Inspect DbTableAiPanelComponent for isExpanded default and any forced expansion logic
rg -n --type=ts 'class\s+DbTableAiPanelComponent|DbTableAiPanelComponent|isExpanded' frontend/src/app/components -S
# 3) Show relevant parts of table-state.service.ts around expansion toggling
sed -n '1,220p' frontend/src/app/services/table-state.service.tsRepository: rocket-admin/rocketadmin
Length of output: 4810
🏁 Script executed:
#!/bin/bash
# Inspect DbTableAiPanelComponent subscription and initial isExpanded behavior
sed -n '1,220p' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts
# Inspect db-table-view mobile init around the referenced lines
sed -n '250,360p' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.tsRepository: rocket-admin/rocketadmin
Length of output: 10081
🏁 Script executed:
#!/bin/bash
# 1) Inspect AI panel template for isExpanded bindings
sed -n '1,220p' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.html
# 2) Find where expansion is toggled in the AI panel component
rg -n --type=ts 'toggleAIPanelExpanded|isExpanded\s*=' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts -S
# 3) Find where closeAIpanel() is called (beyond mobile init)
rg -n --type=ts 'closeAIpanel\(' frontend/src/app -SRepository: rocket-admin/rocketadmin
Length of output: 6711
🏁 Script executed:
#!/bin/bash
# 1) Inspect DbTableAiPanelComponent methods around the toggleAIPanelExpanded call
sed -n '280,380p' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.ts
# 2) Inspect db-table-view.component.ts around the mobile init closeAIpanel snippet (lines ~285-330)
sed -n '280,340p' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts
# 3) Inspect how DbTableAiPanelComponent is rendered / gated in db-table-view.component.html
# (this is crucial to understand what "closing" does visually)
rg -n --type=ts 'app-db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view -S --glob='*.html'Repository: rocket-admin/rocketadmin
Length of output: 4939
🏁 Script executed:
#!/bin/bash
# Inspect how DbTableAiPanelComponent is rendered/gated (likely by aiPanelCast / isAIpanelOpened)
rg -n --type=ts 'isAIpanelOpened|aiPanelCast|handleViewAIpanel|closeAIpanel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts
# Inspect db-table-view template for DbTableAiPanelComponent usage and any *ngIf / [ngClass] gating
sed -n '1,260p' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
rg -n --type=ts 'DbTableAiPanelComponent' frontend/src/app/components/dashboard/db-table-view -S --glob='*.html'
# Also check table-ai-panel component top-level wrapper for isAIpanelOpened usage (if any)
rg -n --type=html 'isAIpanelOpened' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 15197
🏁 Script executed:
#!/bin/bash
# 1) Find where DbTableAiPanelComponent is used in the parent template
rg -n --type=html 'db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
# 2) Show the surrounding lines for the first occurrence(s)
rg -n --type=html 'db-table-ai-panel' -n frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html | head -n 20 | cut -d: -f1 | while read -r ln; do
start=$((ln-40)); end=$((ln+40));
echo "---- around line $ln ----"
sed -n "${start},${end}p" frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
done
# 3) Inspect the mobile init closeAIpanel call site in db-table-view.component.ts around the reported lines (~305)
sed -n '260,340p' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.tsRepository: rocket-admin/rocketadmin
Length of output: 2747
🏁 Script executed:
#!/bin/bash
# Locate where the AI panel component is rendered in the table view template
rg -n --type=html 'app-db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
# Show ~120 lines around each match (if multiple)
rg -n --type=html 'app-db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html | cut -d: -f1 | while read -r ln; do
start=$((ln-60)); end=$((ln+60));
echo "---- around line $ln ----"
sed -n "${start},${end}p" frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
done
# Inspect db-table-view component for aiPanelCast subscription / isAIpanelOpened usage
rg -n --type=ts 'aiPanelCast|aiPanelSubject|isAIpanelOpened|aiPanelExpandedCast' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts
# Inspect ai panel template for any toggle/collapse UI wiring
rg -n --type=html 'toggleExpand\(|isExpanded|ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 650
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find actual render of the AI panel component in the table view template
rg -n --type=html '<app-db-table-ai-panel|db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
# If found, dump a window around the match(s)
rg -n --type=html '<app-db-table-ai-panel' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html | cut -d: -f1 | while read -r ln; do
start=$((ln-80)); end=$((ln+80));
echo "---- around line $ln ----"
sed -n "${start},${end}p" frontend/src/app/components/dashboard/db-table-view/db-table-view.component.html
done
# Check table view component for any aiPanelCast -> isAIpanelOpened wiring
rg -n --type=ts 'aiPanelCast|aiPanelExpandedCast|isAIpanelOpened|aiPanelOpened' frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts
# Check AI panel template for any expansion/collapse wiring and whether it depends on isExpanded or isAIpanelOpened
rg -n --type=html 'toggleExpand|isExpanded|isAIpanelOpened|handleClose|mat-icon:|ai-panel-sidebar' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 50
🏁 Script executed:
#!/bin/bash
# Search whole frontend for the AI panel selector usage
rg -n --type=html '<app-db-table-ai-panel|db-table-ai-panel' frontend/src/app
# Find where isAIpanelOpened is used in any template
rg -n --type=html 'isAIpanelOpened' frontend/src/app/components/dashboard
# Search inside the AI panel template for handleClose/toggleExpand and any *ngIf gating
rg -n --type=html 'handleClose|toggleExpand|toggleAIPanelExpanded|*ngIf=.*AI|*ngIf=.*expanded|isExpanded' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 651
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect dashboard template where the panel is gated by isAIpanelOpened
sed -n '110,170p' frontend/src/app/components/dashboard/dashboard.component.html
# 2) Inspect dashboard component TS to see how isAIpanelOpened is set (likely subscription to TableStateService.aiPanelCast)
rg -n --type=ts 'isAIpanelOpened|aiPanelCast|TableStateService' frontend/src/app/components/dashboard/dashboard.component.ts
sed -n '1,140p' frontend/src/app/components/dashboard/dashboard.component.ts
# 3) Inspect AI panel template for expansion toggle wiring
rg -n --type=html 'toggleExpand\(|toggleAIPanelExpanded|toggle.*Expand|isExpanded' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 7706
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect dashboard component for isAIpanelOpened subscription logic
rg -n --type=ts 'aiPanelCast|isAIpanelOpened\s*=' frontend/src/app/components/dashboard/dashboard.component.ts
sed -n '130,200p' frontend/src/app/components/dashboard/dashboard.component.tsRepository: rocket-admin/rocketadmin
Length of output: 2324
🏁 Script executed:
#!/bin/bash
# Find all call sites that can open/close the AI panel
rg -n --type=ts 'handleViewAIpanel\(|closeAIpanel\(|toggleAIPanelExpanded\(' frontend/src/app
# See if any code restores expanded/open state from storage
rg -n --type=ts 'aiPanel(Expanded)?(Cast|Subject)|aiPanelExpandedSubject|aiPanelSubject' frontend/src/app/services frontend/src/app/componentsRepository: rocket-admin/rocketadmin
Length of output: 3001
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n --type=html 'toggleExpand\(|toggleAIPanelExpanded\(|isExpanded' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.htmlRepository: rocket-admin/rocketadmin
Length of output: 262
🏁 Script executed:
#!/bin/bash
# Find any template wiring for toggleExpand()
rg -n --type=ts 'toggleExpand\(' frontend/src/app/components/dashboard/db-table-view -S
rg -n --type=html 'toggleExpand\(' frontend/src/app/components/dashboard/db-table-view -S
rg -n --type=html 'toggleAIPanelExpanded\(' frontend/src/app/components/dashboard/db-table-view -S
rg -n --type=html 'isExpanded' frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel -SRepository: rocket-admin/rocketadmin
Length of output: 591
Mobile UX for AI panel is consistent (but expansion state ignores the emitted value)
On mobile init, DbTableViewComponent calls TableStateService.closeAIpanel(), which sets aiPanelSubject to false; DashboardComponent renders <app-db-table-ai-panel> only when isAIpanelOpened (from aiPanelCast) is true, so the panel won’t be shown on initial load.
When the user opens the AI panel, DbTableAiPanelComponent mounts with isExpanded = true, and its aiPanelExpandedCast subscription currently forces this.isExpanded = true regardless of whether the cast emits false—so any intended “collapse” behavior from toggleAIPanelExpanded() / toggleExpand() won’t be respected if you wire it up.
🤖 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.ts`
around lines 305 - 308, DbTableAiPanelComponent's aiPanelExpandedCast
subscription ignores emitted values and always sets this.isExpanded = true, so
collapse events from TableStateService (aiPanelSubject) and
toggleAIPanelExpanded()/toggleExpand() are not respected; update the
subscription in DbTableAiPanelComponent to set this.isExpanded = emittedValue
(or its boolean negation where intended) instead of forcing true, ensuring it
reads the incoming value from aiPanelExpandedCast and updates the panel
expansion state accordingly; verify TableStateService.closeAIpanel() and
aiPanelCast/isAIpanelOpened logic remain unchanged so the component simply
mirrors the emitted expansion boolean.
| .mobile-card { | ||
| background: var(--mat-table-background-color, #fff); | ||
| border: none; | ||
| border-radius: 12px; | ||
| padding: 12px 16px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 8px; | ||
| } |
There was a problem hiding this comment.
Replace hardcoded color with palette variable.
Line 40 uses a hardcoded #fff color as the fallback value for --mat-table-background-color. As per coding guidelines, only colors from generated palettes should be used. Replace the hardcoded color with a palette CSS variable reference.
🎨 Proposed fix
.mobile-card {
- background: var(--mat-table-background-color, `#fff`);
+ background: var(--mat-table-background-color, var(--color-whitePalette-500));
border: none;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."
📝 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.
| .mobile-card { | |
| background: var(--mat-table-background-color, #fff); | |
| border: none; | |
| border-radius: 12px; | |
| padding: 12px 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .mobile-card { | |
| background: var(--mat-table-background-color, var(--color-whitePalette-500)); | |
| border: none; | |
| border-radius: 12px; | |
| padding: 12px 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } |
🤖 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/skeletons/placeholder-table-data/placeholder-table-data.component.css`
around lines 39 - 47, The .mobile-card rule in
placeholder-table-data.component.css uses a hardcoded fallback "`#fff`" for
--mat-table-background-color; replace that hardcoded value with a generated
palette CSS variable from the theme (the palette variables defined in
colorConfig/@brumeilde/ngx-theme) so the fallback becomes a palette reference
(e.g., use var(--mat-table-background-color, <palette-var>)); update the
.mobile-card declaration in placeholder-table-data.component.css to reference
the appropriate palette variable instead of "`#fff`".
| <div class="mat-elevation-z4 data-table data-table_desktop"> | ||
| <div *ngFor="let _ of numberOfDivs; let i = index" class="table-cell"> | ||
| <div class="bone table-cell-content"></div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="data-table_mobile"> | ||
| <div *ngFor="let _ of numberOfCards; let i = index" class="mobile-card mat-elevation-z2"> | ||
| <div class="bone mobile-card__value mobile-card__value_short"></div> | ||
| <div class="bone mobile-card__value"></div> | ||
| <div class="bone mobile-card__value mobile-card__value_medium"></div> | ||
| <div class="bone mobile-card__value mobile-card__value_long"></div> | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
Migrate to Angular 19 built-in control flow syntax.
Lines 2 and 8 use the *ngFor structural directive. As per coding guidelines, new code should use Angular 19's built-in control flow (@for) instead of structural directives.
♻️ Proposed migration to `@for` syntax
<div class="mat-elevation-z4 data-table data-table_desktop">
- <div *ngFor="let _ of numberOfDivs; let i = index" class="table-cell">
+ `@for` (_ of numberOfDivs; track $index) {
+ <div class="table-cell">
<div class="bone table-cell-content"></div>
</div>
+ }
</div>
<div class="data-table_mobile">
- <div *ngFor="let _ of numberOfCards; let i = index" class="mobile-card mat-elevation-z2">
+ `@for` (_ of numberOfCards; track $index) {
+ <div class="mobile-card mat-elevation-z2">
<div class="bone mobile-card__value mobile-card__value_short"></div>
<div class="bone mobile-card__value"></div>
<div class="bone mobile-card__value mobile-card__value_medium"></div>
<div class="bone mobile-card__value mobile-card__value_long"></div>
</div>
+ }
</div>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."
📝 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.
| <div class="mat-elevation-z4 data-table data-table_desktop"> | |
| <div *ngFor="let _ of numberOfDivs; let i = index" class="table-cell"> | |
| <div class="bone table-cell-content"></div> | |
| </div> | |
| </div> | |
| <div class="data-table_mobile"> | |
| <div *ngFor="let _ of numberOfCards; let i = index" class="mobile-card mat-elevation-z2"> | |
| <div class="bone mobile-card__value mobile-card__value_short"></div> | |
| <div class="bone mobile-card__value"></div> | |
| <div class="bone mobile-card__value mobile-card__value_medium"></div> | |
| <div class="bone mobile-card__value mobile-card__value_long"></div> | |
| </div> | |
| </div> | |
| <div class="mat-elevation-z4 data-table data-table_desktop"> | |
| `@for` (_ of numberOfDivs; track $index) { | |
| <div class="table-cell"> | |
| <div class="bone table-cell-content"></div> | |
| </div> | |
| } | |
| </div> | |
| <div class="data-table_mobile"> | |
| `@for` (_ of numberOfCards; track $index) { | |
| <div class="mobile-card mat-elevation-z2"> | |
| <div class="bone mobile-card__value mobile-card__value_short"></div> | |
| <div class="bone mobile-card__value"></div> | |
| <div class="bone mobile-card__value mobile-card__value_medium"></div> | |
| <div class="bone mobile-card__value mobile-card__value_long"></div> | |
| </div> | |
| } | |
| </div> |
🧰 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/skeletons/placeholder-table-data/placeholder-table-data.component.html`
around lines 1 - 14, Replace the Angular structural directives *ngFor with
Angular 19 built-in control flow `@for` for both iterations over numberOfDivs and
numberOfCards: locate the two occurrences using the variables numberOfDivs and
numberOfCards and convert the templates to use `@for` (the new for-loop syntax) so
the repeated .table-cell and .mobile-card elements are produced by `@for` instead
of *ngFor; keep the same loop index (i) and class structure and ensure
bindings/loop variables are updated to the `@for` syntax.
| .user__email { | ||
| font-size: 12px; | ||
| color: rgba(0, 0, 0, 0.64); | ||
| } | ||
|
|
||
| @media (prefers-color-scheme: dark) { | ||
| .user__email { | ||
| color: rgba(255, 255, 255, 0.64); | ||
| } | ||
| } |
There was a problem hiding this comment.
Replace hardcoded colors with palette CSS variables.
The .user__email color uses hardcoded rgba() values instead of referencing palette variables. As per coding guidelines, only use colors from generated palettes defined in src/main.ts (colorConfig) and reference palette CSS variables from @brumeilde/ngx-theme.
🎨 Proposed fix using palette variables
.user__email {
font-size: 12px;
- color: rgba(0, 0, 0, 0.64);
+ color: var(--color-on-surface-variant);
}
`@media` (prefers-color-scheme: dark) {
.user__email {
- color: rgba(255, 255, 255, 0.64);
+ color: var(--color-on-surface-variant);
}
}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.
🤖 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/users/users.component.css` around lines 127 -
136, The .user__email rule uses hardcoded rgba colors; update both the default
and `@media` (prefers-color-scheme: dark) selectors to use the generated palette
CSS variables from `@brumeilde/ngx-theme` (as defined by colorConfig in
src/main.ts) instead of rgba values — replace the light-mode color with the
appropriate palette var (e.g., a neutral/text-muted variable) and the dark-mode
color with the corresponding dark-theme palette var so both .user__email
declarations reference palette CSS variables rather than hardcoded rgba().
| <button matListItemMeta type="button" mat-icon-button | ||
| angulartics2On="click" | ||
| angularticsAction="Users access: delete user is clicked" | ||
| matTooltip="Delete user" | ||
| (click)="openDeleteUserDialog(user, groupItem.group); posthog.capture('Users access: delete user is clicked')"> | ||
| <mat-icon>person_remove</mat-icon> | ||
| </button> |
There was a problem hiding this comment.
Remove obsolete matListItemMeta directive.
The matListItemMeta directive is specific to Angular Material's mat-list-item component and has no effect on a plain <button> element inside a semantic <li>. Since the template was refactored to use <ul> and <li> instead of mat-list, this attribute should be removed.
🧹 Proposed cleanup
- <button matListItemMeta type="button" mat-icon-button
+ <button type="button" mat-icon-button
angulartics2On="click"
angularticsAction="Users access: delete user is clicked"
matTooltip="Delete user"
(click)="openDeleteUserDialog(user, groupItem.group); posthog.capture('Users access: delete user is clicked')">
<mat-icon>person_remove</mat-icon>
</button>📝 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.
| <button matListItemMeta type="button" mat-icon-button | |
| angulartics2On="click" | |
| angularticsAction="Users access: delete user is clicked" | |
| matTooltip="Delete user" | |
| (click)="openDeleteUserDialog(user, groupItem.group); posthog.capture('Users access: delete user is clicked')"> | |
| <mat-icon>person_remove</mat-icon> | |
| </button> | |
| <button type="button" mat-icon-button | |
| angulartics2On="click" | |
| angularticsAction="Users access: delete user is clicked" | |
| matTooltip="Delete user" | |
| (click)="openDeleteUserDialog(user, groupItem.group); posthog.capture('Users access: delete user is clicked')"> | |
| <mat-icon>person_remove</mat-icon> | |
| </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/users/users.component.html` around lines 113 -
119, Remove the obsolete matListItemMeta attribute from the delete button in the
users template: locate the <button> that calls openDeleteUserDialog(user,
groupItem.group) and posthog.capture(...) and delete the matListItemMeta
directive (leave mat-icon-button, angulartics2On, angularticsAction, matTooltip
and the (click) handler intact) so the button is plain HTML inside the <li>
after the refactor from mat-list to ul/li.
…licy editor The "Add policy" button binding read policyList?.policies()?.length and policyList?.showAddForm — a @ViewChild query that resolves mid change-detection cycle, causing the value to change after it was checked. Use the parent-owned policyItems().length instead, and convert showAddForm to a signal so it reads reactively with a stable initial value. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary by CodeRabbit
New Features
Improvements
UX