Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/src/entities/company-info/company-info.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ import {
IRemoveUserFromCompany,
IRevokeUserInvitationInCompany,
ISuspendUsersInCompany,
IUnsuspendUsersInCompany,
IToggleCompanyTestConnectionsMode,
IUnsuspendUsersInCompany,
IUpdateCompanyName,
IUpdateUsers2faStatusInCompany,
IUpdateUsersCompanyRoles,
Expand Down Expand Up @@ -173,6 +173,7 @@ export class CompanyInfoController {
description: 'Get company name by id.',
type: FoundCompanyNameDs,
})
@Throttle({ default: { limit: isTest() ? 200 : 10, ttl: 60000 } })
@Get('name/:companyId')
async getCompanyNameById(@Param('companyId') companyId: string): Promise<FoundCompanyNameDs> {
return await this.getCompanyNameUseCase.execute(companyId);
Expand Down
3 changes: 3 additions & 0 deletions backend/src/entities/email/email-verification.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export class EmailVerificationEntity {
@Column({ default: null })
verification_string: string;

@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;

@OneToOne(
() => UserEntity,
(user) => user.email_verification,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Constants } from '../../../helpers/constants/constants.js';
import { Encryptor } from '../../../helpers/encryption/encryptor.js';
import { UserEntity } from '../../user/user.entity.js';
import { EmailVerificationEntity } from '../email-verification.entity.js';
Expand All @@ -8,6 +9,9 @@ export const emailVerificationRepositoryExtension = {
.leftJoinAndSelect('email_verification.user', 'user')
.where('email_verification.verification_string = :ver_string', {
ver_string: verificationString,
})
.andWhere('email_verification.createdAt >= :valid_after', {
valid_after: Constants.ONE_DAY_AGO(),
});
return await qb.getOne();
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export class PasswordResetEntity {
@Column({ default: null })
verification_string: string;

@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
Comment on lines +12 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

PostgreSQL TypeORM timestamp without time zone comparison with JS Date UTC behavior

💡 Result:

When using PostgreSQL's "timestamp without time zone" column type with TypeORM, the library often experiences behavior that results in unexpected time offsets because it treats these columns as "naive" (lacking timezone information) [1][2][3]. Key behaviors and technical details include: 1. Naive Interpretation: PostgreSQL "timestamp without time zone" stores date and time values without any timezone offset [4][5]. When TypeORM fetches this data, it frequently interprets the value relative to the local time of the Node.js process rather than UTC, or vice versa, leading to shifts based on the server's or database's timezone settings [1][2][3]. 2. Implicit Conversion: During read/write operations, TypeORM or the underlying PostgreSQL driver (pg) may apply timezone offsets when converting between the database's format and JavaScript Date objects [6][3]. Because "timestamp without time zone" lacks an explicit offset, the driver or ORM assumes a timezone, often resulting in "off-by-hours" issues if the local system timezone deviates from the intended storage timezone [2][3]. 3. Recommended Best Practice: The most authoritative and reliable way to avoid this ambiguity is to use "timestamp with time zone" (frequently aliased as "timestamptz" in PostgreSQL) for all timestamp columns [7][1][2][3]. This column type stores a point in time internally as UTC, which allows TypeORM to map the value to a JavaScript Date object consistently without implicit shifts [4][2][5]. If you must use "timestamp without time zone," common workarounds include: - Setting the TZ environment variable to UTC (e.g., TZ=UTC node main.ts) for your Node.js process to ensure consistency [1][3]. - Explicitly configuring the database driver to parse dates as UTC [1]. - Using @Column({ type: 'timestamptz' }) or @CreateDateColumn({ type: 'timestamptz' }) in your entity definitions to force correct timezone handling, even if the underlying column in legacy schemas is "timestamp" [2][3].

Citations:


🏁 Script executed:

find backend -name "password-reset.entity.ts" -o -name "*email-verification*entity.ts" | head -20

Repository: rocket-admin/rocketadmin

Length of output: 189


🏁 Script executed:

grep -r "Constants.ONE_DAY_AGO" backend --include="*.ts" -A 3 -B 3 | head -50

Repository: rocket-admin/rocketadmin

Length of output: 2556


🏁 Script executed:

grep -r "createdAt" backend/src/entities/user --include="*.ts" -A 2 -B 2 | grep -E "(timestamp|createdAt)" | head -20

Repository: rocket-admin/rocketadmin

Length of output: 2376


🏁 Script executed:

grep -r "ONE_DAY_AGO" backend --include="*.ts" -B 2 -A 2 | head -40

Repository: rocket-admin/rocketadmin

Length of output: 2215


🏁 Script executed:

cat backend/src/entities/email/email-verification.entity.ts

Repository: rocket-admin/rocketadmin

Length of output: 627


🏁 Script executed:

cat backend/src/entities/user/user-password/password-reset.entity.ts

Repository: rocket-admin/rocketadmin

Length of output: 610


Use timestamptz to avoid timezone-dependent comparison issues.

createdAt is declared as timestamp (without time zone) but is compared downstream against Constants.ONE_DAY_AGO() (a JS Date/UTC instant). If the database session timezone is not UTC, the createdAt >= :valid_after comparison can be skewed by the timezone offset, causing password reset and email verification links to expire earlier or later than the intended 24-hour window. Using timestamptz ensures the timestamp is stored and compared as UTC, making the comparison unambiguous. This applies to both password-reset.entity.ts and email-verification.entity.ts.

🤖 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 `@backend/src/entities/user/user-password/password-reset.entity.ts` around
lines 12 - 13, The createdAt column is currently defined with type 'timestamp'
which is timezone-naive; update the `@Column` decorator for the createdAt property
in password-reset.entity.ts (and likewise in email-verification.entity.ts) to
use type: 'timestamptz' so DB stores UTC-aware timestamps, ensuring comparisons
against Constants.ONE_DAY_AGO() (and any createdAt >= :valid_after queries)
behave correctly across timezones; keep the property name createdAt and only
change the column type definition in the `@Column`(...) metadata.


@OneToOne(
() => UserEntity,
(user) => user.password_reset,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Constants } from '../../../../helpers/constants/constants.js';
import { Encryptor } from '../../../../helpers/encryption/encryptor.js';
import { UserEntity } from '../../user.entity.js';
import { PasswordResetEntity } from '../password-reset.entity.js';
Expand All @@ -8,6 +9,9 @@ export const userPasswordResetCustomRepositoryExtension = {
.leftJoinAndSelect('password_reset.user', 'user')
.where('password_reset.verification_string = :ver_string', {
ver_string: verificationString,
})
.andWhere('password_reset.createdAt >= :valid_after', {
valid_after: Constants.ONE_DAY_AGO(),
});
return await qb.getOne();
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BadRequestException } from '@nestjs/common';
import { ConnectionTypesEnum } from '@rocketadmin/shared-code/dist/src/shared/enums/connection-types-enum.js';
import { isReadOnlyMongoAggregationPipeline } from '../../../../ai-core/tools/query-validators.js';
import { slackPostMessage } from '../../../../helpers/slack/slack-post-message.js';

const FORBIDDEN_SQL_KEYWORDS = [
Expand Down Expand Up @@ -175,6 +176,14 @@ export function checkMongoQueryIsSafe(query: string): QuerySafetyResult {
}
}

if (!isReadOnlyMongoAggregationPipeline(query)) {
return {
isSafe: false,
reason:
'Query must be a read-only aggregation pipeline (write stages or server-side JavaScript operators such as $out, $merge, $function, $accumulator, $where are not allowed)',
};
}
Comment on lines +179 to +185

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the validator implementation and its expected input type.
fd -t f 'query-validators.ts' --exec cat {}
echo "----- callers of isReadOnlyMongoAggregationPipeline -----"
rg -nP '\bisReadOnlyMongoAggregationPipeline\s*\(' -C3
echo "----- existing tests / sample Mongo queries for visualization panels -----"
rg -nP 'checkMongoQueryIsSafe' -C3

Repository: rocket-admin/rocketadmin

Length of output: 5485


Ensure isReadOnlyMongoAggregationPipeline is implemented/exported and accepts the raw Mongo query string (don’t accidentally reject valid non-aggregation reads).

  • backend/src/entities/visualizations/panel/utils/check-query-is-safe.util.ts calls isReadOnlyMongoAggregationPipeline(query) at lines 179-185, but backend/src/ai-core/tools/query-validators.ts does not export isReadOnlyMongoAggregationPipeline (only isValidSQLQuery, isValidMongoDbCommand, wrapQueryWithLimit, cleanAIJsonResponse are present). Confirm the imported .../query-validators.js actually contains this function (or fix the import/export mismatch), otherwise the safety check may fail at build/runtime.
  • After locating the correct validator, confirm its input contract (does it parse the raw string itself vs expecting a parsed pipeline) and whether it incorrectly rejects legitimate read-only non-aggregation queries (e.g., find/distinct) for visualization panels.
🤖 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 `@backend/src/entities/visualizations/panel/utils/check-query-is-safe.util.ts`
around lines 179 - 185, The safety check calls
isReadOnlyMongoAggregationPipeline(query) but that symbol is not exported from
backend/src/ai-core/tools/query-validators.ts and/or may expect a parsed
pipeline rather than the raw query string; fix by either exporting
isReadOnlyMongoAggregationPipeline from query-validators (or re-importing the
correct module that defines it), or change the call in
check-query-is-safe.util.ts to the appropriate validator (e.g.,
isValidMongoDbCommand or a new isReadOnlyMongoQuery) that accepts raw strings;
ensure the validator's contract is respected (parse the string before calling if
the validator expects a parsed pipeline) and broaden the check to allow
legitimate read-only non-aggregation queries like find/distinct so visualization
panels aren't incorrectly rejected.


return { isSafe: true };
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddCreatedAtColumnsIntoPasswordResetAndEmailVerificationEntities1780411916141
implements MigrationInterface
{
name = 'AddCreatedAtColumnsIntoPasswordResetAndEmailVerificationEntities1780411916141';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "email_verification" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT now()`);
await queryRunner.query(`ALTER TABLE "password_reset" ADD "createdAt" TIMESTAMP NOT NULL DEFAULT now()`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "password_reset" DROP COLUMN "createdAt"`);
await queryRunner.query(`ALTER TABLE "email_verification" DROP COLUMN "createdAt"`);
}
}
8 changes: 4 additions & 4 deletions frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
},
{
"type": "anyComponentStyle",
"maximumWarning": "10kb",
"maximumError": "20kb"
"maximumWarning": "20kb",
"maximumError": "32kb"
Comment on lines +78 to +79

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find the largest component style files and check for potential duplication

echo "=== Top 20 largest component style files ==="
fd -e scss -e css --type f -E 'node_modules' -E 'dist' | \
  xargs wc -c | \
  sort -rn | \
  head -n 21

echo -e "\n=== Components with styles over 10kb ==="
fd -e scss -e css --type f -E 'node_modules' -E 'dist' | \
  while read file; do
    size=$(wc -c < "$file")
    if [ "$size" -gt 10240 ]; then
      echo "$size bytes - $file"
    fi
  done | sort -rn

echo -e "\n=== Check for duplicate CSS patterns (common selectors) ==="
rg -t scss -t css --no-filename -oP '^\.[a-z-]+\s*\{' frontend/src | \
  sort | uniq -c | sort -rn | head -n 20

Repository: rocket-admin/rocketadmin

Length of output: 2781


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== angular.json anyComponentStyle (lines 70-90) ==="
nl -ba frontend/angular.json | sed -n '70,90p'

echo "=== angular.json anyComponentStyle (lines 120-150) ==="
nl -ba frontend/angular.json | sed -n '120,150p'

echo "=== Component style sizes over thresholds (CSS/SCSS under frontend/src) ==="
# Count/list sizes; include both .css and .scss; exclude node_modules/dist
mapfile -t files < <(fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path)

python3 - <<'PY'
import os
files = os.environ.get("FILES","")
PY

python3 - <<'PY'
import os, sys, subprocess, json
# Re-run fd within python to avoid shell env passing
import subprocess
res = subprocess.check_output(["bash","-lc", "fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path"], text=True)
paths = [p for p in res.splitlines() if p.strip()]
items=[]
for p in paths:
    try:
        items.append((os.path.basename(p), p, os.path.getsize(p)))
    except Exception:
        pass
items.sort(key=lambda x: x[2], reverse=True)

def summarize(th):
    over=[x for x in items if x[2]>th]
    over.sort(key=lambda x:x[2], reverse=True)
    print(f"\n=== count > {th} bytes ({th/1024:.1f}kb) ===")
    print(len(over))
    for name,p,size in over[:20]:
        print(f"{size} bytes - {p}")

summarize(10240)  # 10kb
summarize(20480)  # 20kb
summarize(32768)  # 32kb

print("\n=== Top 15 ===")
for name,p,size in items[:15]:
    print(f"{size} bytes - {p}")
PY

echo "=== Duplicate selector-ish patterns check (no rg -t scss/cs filetype) ==="
# Look for repeated simple class selectors blocks; exclude node_modules/dist
# (This is a heuristic; it won't prove duplication, but it avoids the prior rg filetype failure.)
fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path | \
  xargs rg -n --no-filename -o '^\.[A-Za-z_-][A-Za-z0-9_-]*\s*\{' | \
  sort | uniq -c | sort -nr | head -n 20

Repository: rocket-admin/rocketadmin

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== angular.json anyComponentStyle (production) around lines 70-95 ==="
awk 'NR>=70 && NR<=95 {printf "%d:%s\n", NR, $0}' frontend/angular.json

echo "=== angular.json anyComponentStyle (saas-production) around lines 120-155 ==="
awk 'NR>=120 && NR<=155 {printf "%d:%s\n", NR, $0}' frontend/angular.json

echo "=== Component style sizes (CSS/SCSS under frontend/src) > 10kb / 20kb / 32kb ==="
# Use fd under frontend/src so we don't scan node_modules/dist
mapfile -t paths < <(fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path)

python3 - <<'PY'
import os, sys
paths = os.environ.get("PATHS","").splitlines()
# (If PATHS isn't set, fall back to reading from stdin)
if not paths or paths==['']:
    paths = sys.stdin.read().splitlines()

items=[]
for p in paths:
    p=p.strip()
    if not p: 
        continue
    try:
        items.append((os.path.getsize(p), p))
    except OSError:
        pass
items.sort(reverse=True)

def show(th):
    over=[(s,p) for (s,p) in items if s>th]
    print(f"\n=== count > {th} bytes ({th/1024:.1f}kb) ===")
    for s,p in over[:25]:
        print(f"{s} bytes - {p}")
show(10*1024)
show(20*1024)
show(32*1024)

print("\n=== Top 20 by size ===")
for s,p in items[:20]:
    print(f"{s} bytes - {p}")
PY <<EOF
$(printf "%s\n" "${paths[@]}")
EOF

echo "=== Selector-block duplication heuristic (counts repeated simple class selectors) ==="
# Avoid rg -t (filetype) issues by using globs on extensions.
fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path | \
  xargs rg -n --no-filename -o --glob '*.scss' --glob '*.css' '^\.[A-Za-z_-][A-Za-z0-9_-]*[[:space:]]*\{' 2>/dev/null | \
  sort | uniq -c | sort -nr | head -n 30

Repository: rocket-admin/rocketadmin

Length of output: 2133


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Git diff for anyComponentStyle budgets (frontend/angular.json) ==="
# Show only the relevant portion of the diff to capture the previous values
git diff -U3 -- frontend/angular.json | sed -n '/anyComponentStyle/,+30p'

echo "=== Clean size listing: CSS/SCSS under frontend/src, >10kb ==="
fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path | \
  while IFS= read -r f; do
    [ -f "$f" ] || continue
    sz=$(wc -c < "$f" 2>/dev/null || echo 0)
    if [ "$sz" -gt 10240 ]; then
      printf "%s\t%s\n" "$sz" "$f"
    fi
  done | sort -nr | head -n 50

echo "=== Count of files >10kb / >20kb / >32kb ==="
fd -e css -e scss --type f -E 'node_modules' -E 'dist' frontend/src --full-path | \
  while IFS= read -r f; do
    [ -f "$f" ] || continue
    sz=$(wc -c < "$f" 2>/dev/null || echo 0)
    if [ "$sz" -gt 10240 ]; then echo "gt10:$f:$sz"; fi
    if [ "$sz" -gt 20480 ]; then echo "gt20:$f:$sz"; fi
    if [ "$sz" -gt 32768 ]; then echo "gt32:$f:$sz"; fi
  done | awk -F: '{c[$1]++} END {for(k in c) print k,c[k]}' | sort -t' ' -k2 -nr

Repository: rocket-admin/rocketadmin

Length of output: 1039


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for anyComponentStyle warning/error values in frontend/angular.json ==="
rg -n --no-heading 'type": "anyComponentStyle"|maximumWarning|maximumError' frontend/angular.json

echo "=== Search for '10kb' occurrences in frontend/angular.json ==="
rg -n --no-heading '10kb' frontend/angular.json || true

echo "=== Duplicate selector-ish heuristic: repeated simple class selector blocks ==="
# Heuristic: count top occurrences of ".class {"
fd --type f -e css -e scss --full-path --exclude node_modules --exclude dist frontend/src | \
  xargs rg -n --no-filename -o '^\.[A-Za-z_-][A-Za-z0-9_-]*[[:space:]]*\{' 2>/dev/null | \
  sed 's/.*\(\.[A-Za-z_-][A-Za-z0-9_-]*[[:space:]]*{\).*/\1/' | \
  sort | uniq -c | sort -nr | head -n 20

Repository: rocket-admin/rocketadmin

Length of output: 1131


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for anyComponentStyle warning/error values in frontend/angular.json ==="
rg -n --no-heading 'type": "anyComponentStyle"|maximumWarning|maximumError' frontend/angular.json

echo "=== Search for '10kb' occurrences in frontend/angular.json ==="
rg -n --no-heading '10kb' frontend/angular.json || true

echo "=== Duplicate selector-ish heuristic: repeated simple class selector blocks ==="
# Heuristic: count top occurrences of ".class {" across frontend/src (*.css/*.scss), excluding node_modules/dist
fd --type f -e css -e scss --full-path --exclude node_modules --exclude dist frontend/src | \
  xargs rg -n --no-filename -o '^\.[A-Za-z_-][A-Za-z0-9_-]*[[:space:]]*\{' 2>/dev/null | \
  sed 's/.*\(\.[A-Za-z_-][A-Za-z0-9_-]*[[:space:]]*{\).*/\1/' | \
  sort | uniq -c | sort -nr | head -n 20

Repository: rocket-admin/rocketadmin

Length of output: 1131


Investigate component style bloat before further relaxing anyComponentStyle budgets (production/saas-production).

frontend/angular.json sets anyComponentStyle to maximumWarning: 20kb / maximumError: 32kb in both the production and saas-production configurations. In frontend/src, there are currently 2 component style files > 20kb and 1 > 32kb (CSS/SCSS). The selector repetition heuristic also shows common class blocks repeated across styles (e.g., .wrapper, .actions), which may indicate duplication.

Before relying on higher limits for mobile performance, identify the specific component styles crossing these thresholds and determine whether the growth is from duplication/overlapping selectors vs genuinely new UI; then extract shared styles / reduce duplication where possible.

🤖 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/angular.json` around lines 78 - 79, The build budget
anyComponentStyle in frontend/angular.json (maximumWarning: 20kb / maximumError:
32kb) is being hit; locate the specific component style files under frontend/src
that exceed 20kb (and the one >32kb), inspect them for repeated selector blocks
such as .wrapper and .actions, and determine if the excess is from duplicated
shared layout rules versus unique component styling; refactor by extracting
common rules into a shared/global stylesheet or a shared SCSS module and import
it into those components (update component styleUrls to remove duplicated
rules), remove unused selectors and compress verbose selectors, then re-run the
build to confirm budgets are met before considering raising anyComponentStyle
values.

}
]
},
Expand Down Expand Up @@ -129,8 +129,8 @@
},
{
"type": "anyComponentStyle",
"maximumWarning": "10kb",
"maximumError": "20kb"
"maximumWarning": "20kb",
"maximumError": "32kb"
}
]
}
Expand Down
32 changes: 29 additions & 3 deletions frontend/src/app/app.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,34 @@
width: 60vw;
}

.main-menu-sidenav .account-section-item {
--mdc-list-list-item-label-text-size: 13px;
--mat-list-list-item-leading-icon-size: 18px;
--mdc-list-list-item-one-line-container-height: 40px;
--mdc-list-list-item-two-line-container-height: 52px;
}

.main-menu-sidenav .account-section-item .mat-icon {
font-size: 18px;
width: 18px;
height: 18px;
}

@media (width <= 600px) {
.main-menu-sidenav.mat-drawer {
top: 44px !important;
height: calc(100vh - 44px) !important;
}

.main-menu-container ::ng-deep .mat-drawer-backdrop.mat-drawer-shown {
top: 44px !important;
}
Comment on lines +44 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the referenced snippet with line numbers
echo "=== app.component.css: lines around 44-46 ==="
nl -ba frontend/src/app/app.component.css | sed -n '35,60p'

# Confirm how many ::ng-deep occurrences exist in this file
echo "=== Count of ::ng-deep in app.component.css ==="
rg -n "::ng-deep" frontend/src/app/app.component.css || true

# Find all occurrences of ::ng-deep in the frontend (to see if there’s an established pattern)
echo "=== Count of ::ng-deep in frontend ==="
rg -n "::ng-deep" frontend | head -n 50 || true

# Locate Stylelint config files (common locations)
echo "=== Locate stylelint config ==="
ls -la | rg -n "stylelint|\.stylelintrc|stylelint\.json|\.stylelint" || true
find . -maxdepth 4 -type f \( -iname "*stylelint*" -o -iname ".stylelintrc*" -o -iname "*.stylelintrc*" \) | head -n 50 || true

# Print likely stylelint config contents if small; otherwise just grep relevant rules
echo "=== Search stylelint config for ng-deep / deep selectors / unknown pseudo-element ==="
CONFIGS=$(find . -maxdepth 6 -type f \( -iname "*stylelint*" -o -iname ".stylelintrc*" -o -iname "*.stylelintrc*" \) 2>/dev/null | tr '\n' ' ')
if [ -z "${CONFIGS// /}" ]; then
  echo "No stylelint config files found within depth."
else
  # Grep for deep selectors or pseudo-element related rules
  rg -n "ng-deep|::ng-deep|deep\s+selector|pseudo-element|unknown" -S $CONFIGS || true
fi

Repository: rocket-admin/rocketadmin

Length of output: 175


🏁 Script executed:

#!/bin/bash
set -u

FILE="frontend/src/app/app.component.css"

echo "=== app.component.css: lines around 44-46 ==="
python3 - <<'PY'
import itertools
path="frontend/src/app/app.component.css"
start=35
end=60
for i,line in enumerate(open(path,'r',encoding='utf-8',errors='ignore'), start=1):
    if start <= i <= end:
        print(f"{i:4d} {line.rstrip()}")
    if i > end:
        break
PY

echo "=== Count of ::ng-deep in app.component.css ==="
rg -n "::ng-deep" "$FILE" || true

echo "=== Search stylelint config files (shallow) ==="
find . -maxdepth 6 -type f \( -iname "*stylelint*" -o -iname ".stylelintrc*" -o -iname "*.stylelintrc*" \) 2>/dev/null | head -n 50 || true

echo "=== Search stylelint configs for ::ng-deep / deep selector rules ==="
CONFIGS="$(find . -maxdepth 6 -type f \( -iname "*stylelint*" -o -iname ".stylelintrc*" -o -iname "*.stylelintrc*" \) 2>/dev/null)"
if [ -z "${CONFIGS}" ]; then
  echo "No stylelint config files found within depth."
else
  # Grep (via rg) across configs only
  echo "$CONFIGS" | tr ' ' '\n' | xargs -I{} sh -c 'rg -n "ng-deep|::ng-deep|deep selector|pseudo-element|selector-pseudo-element-no-unknown" "{}" || true' | head -n 80
fi

Repository: rocket-admin/rocketadmin

Length of output: 1145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="frontend/src/app/app.component.css"

echo "=== Git diff for app.component.css (if available) ==="
git diff --stat -- "$FILE" || true
git diff -- "$FILE" | sed -n '1,160p' || true

echo "=== Show .stylelintrc.json ==="
cat .stylelintrc.json

echo "=== Stylelint rules related to pseudo-elements/unknown selectors ==="
rg -n "selector-pseudo-element-no-unknown|selector-pseudo-element-colon-notation|pseudo-element|ng-deep|::ng-deep" .stylelintrc.json || true

echo "=== Search other stylelint configs referencing pseudo-element rules ==="
rg -n "selector-pseudo-element-no-unknown|selector-pseudo-element-colon-notation|pseudo-element" -S . || true

echo "=== Count occurrences of ::ng-deep in the repo and list files ==="
rg -n "::ng-deep" frontend | cut -d: -f1 | sort -u

Repository: rocket-admin/rocketadmin

Length of output: 6033


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for Stylelint config overrides/suppressions
rg -n "selector-pseudo-element-no-unknown|selector-pseudo-element-colon-notation|stylelint.*ng-deep|ng-deep.*stylelint" -S . || true

# Check how Stylelint is invoked (CI scripts / package.json)
if [ -f package.json ]; then
  cat package.json | rg -n "\"stylelint\"|lint|styles" -n || true
fi

# Find likely stylelint config usage (common paths)
find . -maxdepth 4 -type f \( -iname "*lint*" -o -iname "*stylelint*" \) | head -n 50 || true

# Show any additional stylelint configs if present
find . -type f \( -iname ".stylelintrc*" -o -iname "*stylelint*.json" -o -iname "*stylelint*.rc*" \) 2>/dev/null | head -n 50 || true

Repository: rocket-admin/rocketadmin

Length of output: 208


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Root package.json scripts (stylelint-related) ==="
if [ -f package.json ]; then
  python3 - <<'PY'
import json
p="package.json"
with open(p,'r',encoding='utf-8') as f: d=json.load(f)
scripts=d.get("scripts",{})
for k,v in scripts.items():
    if "stylelint" in k.lower() or "stylelint" in str(v).lower():
        print(k,":",v)
PY
else
  echo "No root package.json"
fi

echo "=== Frontend package.json scripts (stylelint-related) ==="
if [ -f frontend/package.json ]; then
  python3 - <<'PY'
import json, pathlib
p="frontend/package.json"
with open(p,'r',encoding='utf-8') as f: d=json.load(f)
scripts=d.get("scripts",{})
for k,v in scripts.items():
    if "stylelint" in k.lower() or "stylelint" in str(v).lower():
        print(k,":",v)
PY
else
  echo "No frontend/package.json"
fi

echo "=== Repo-wide: where stylelint is referenced ==="
rg -n "stylelint" -S package.json frontend/package.json . || true

Repository: rocket-admin/rocketadmin

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Repo-wide search for 'stylelint' (all files) ==="
rg -n "stylelint" -S . || true

echo "=== Search CI/workflows for stylelint ==="
find . -maxdepth 4 -type f \( -path "./.github/*" -o -path "./.gitlab/*" \) -print | head -n 200 || true
rg -n "stylelint" -S .github .gitlab 2>/dev/null || true

echo "=== Frontend package.json contents (to confirm scripts) ==="
ls -la frontend/package.json || true
sed -n '1,120p' frontend/package.json 2>/dev/null || true

echo "=== Root package.json contents (to confirm scripts) ==="
ls -la package.json || true
sed -n '1,120p' package.json 2>/dev/null || true

echo "=== Any lint-staged / precommit configs referencing stylelint ==="
ls -la | rg -n "lintstaged|husky|precommit|commitlint|lefthook|prettier|stylelint" || true
find . -maxdepth 3 -type f \( -iname "*husky*" -o -iname "*lint-staged*" -o -iname "*lefthook*" -o -iname "*pre-commit*" -o -iname "*lintstaged*" \) -print 2>/dev/null || true
rg -n "stylelint" -S .husky* .lintstaged* .lintstagedrc* .pre-commit* .lefthook* 2>/dev/null || true

Repository: rocket-admin/rocketadmin

Length of output: 5357


🌐 Web query:

stylelint-config-standard-scss selector-pseudo-element-no-unknown default enabled

💡 Result:

The rule selector-pseudo-element-no-unknown is enabled by default in stylelint-config-standard-scss [1][2]. This is because stylelint-config-standard-scss extends the standard Stylelint configuration (stylelint-config-standard), which includes core Stylelint rules such as selector-pseudo-element-no-unknown to enforce standard CSS practices [1][3][2]. If this rule causes issues in your project (for example, by flagging valid SCSS-specific selectors or non-standard vendor-prefixed pseudo-elements), you can disable or customize it by adding it to the rules section of your Stylelint configuration file [1][4][3]. Example of how to disable the rule: { "extends": "stylelint-config-standard-scss", "rules": { "selector-pseudo-element-no-unknown": null } } Example of how to allow specific pseudo-elements: { "extends": "stylelint-config-standard-scss", "rules": { "selector-pseudo-element-no-unknown": [true, { "ignorePseudoElements": ["your-custom-pseudo-element"] }] } }

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== .husky/pre-commit ==="
if [ -f .husky/pre-commit ]; then
  cat .husky/pre-commit
else
  echo "Missing .husky/pre-commit"
fi

echo "=== Find stylelint usage in GitHub workflows ==="
rg -n "stylelint|ng lint|lint:" .github/workflows -S || true

echo "=== Show likely front-end quality workflow files ==="
for f in .github/workflows/frontend.yml .github/workflows/code-quality.yml .github/workflows/build.yml .github/workflows/build-agent.yml; do
  if [ -f "$f" ]; then
    echo "---- $f ----"
    sed -n '1,220p' "$f"
  fi
done

Repository: rocket-admin/rocketadmin

Length of output: 12806


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether stylelint is a declared dependency anywhere
echo "=== Search for stylelint in package manifests ==="
rg -n "\"stylelint\"|stylelint-config|stylelint-" package.json frontend/package.json .github/workflows -S || true

echo "=== Search for stylelint in pnpm/lockfiles ==="
ls -ლა | rg -i "pnpm-lock|yarn-lock|package-lock" || true
find . -maxdepth 3 -type f \( -iname "*pnpm-lock*.yaml" -o -iname "*yarn.lock" -o -iname "*package-lock*.json" \) | head -n 20 | while read -r f; do
  echo "--- $f ---"
  rg -n "stylelint" "$f" -S || true
done

# Inspect Angular lint config (if any) for stylelint usage
echo "=== Inspect Angular workspace config ==="
if [ -f frontend/angular.json ]; then
  sed -n '1,200p' frontend/angular.json
  rg -n "stylelint|lintOptions|style" frontend/angular.json -S || true
fi
if [ -f frontend/.angular-cli.json ]; then
  cat frontend/.angular-cli.json
fi

Repository: rocket-admin/rocketadmin

Length of output: 6299


Remove ::ng-deep from frontend/src/app/app.component.css (lines 44-46)

::ng-deep in the backdrop override (.main-menu-container ::ng-deep .mat-drawer-backdrop...) will be flagged by Stylelint because .stylelintrc.json extends stylelint-config-standard-scss without disabling selector-pseudo-element-no-unknown. Move this backdrop override to a global stylesheet (or otherwise apply it outside view encapsulation) to avoid deep selectors.

🧰 Tools
🪛 Stylelint (17.12.0)

[error] 44-44: Unknown pseudo-element selector "::ng-deep" (selector-pseudo-element-no-unknown)

(selector-pseudo-element-no-unknown)

🤖 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.css` around lines 44 - 46, Remove the deep
selector from the component stylesheet: delete the ::ng-deep token from the rule
in .main-menu-container ::ng-deep .mat-drawer-backdrop.mat-drawer-shown and move
that selector (without ::ng-deep) into your global stylesheet so it applies
outside Angular view encapsulation; keep the rule body (top: 44px !important;)
and ensure the selector is exactly .main-menu-container
.mat-drawer-backdrop.mat-drawer-shown in the global styles to avoid stylelint
error (do not disable the lint rule).


.main-menu-sidenav mat-toolbar {
display: none !important;
}
}

.nav-bar {
position: sticky;
top: 0;
Expand Down Expand Up @@ -218,8 +246,6 @@
display: flex;
flex-direction: column;
align-items: flex-start;
border-top: var(--mat-table-row-item-outline-width, 1px) solid
var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12));
border-bottom: var(--mat-table-row-item-outline-width, 1px) solid
var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12));
margin-left: 8px;
Expand All @@ -243,7 +269,7 @@
}

.connection-navigation__upgrade-button {
margin-top: 8px;
margin-top: -12px;
margin-left: 8px;
width: calc(100% - 16px);
}
Expand Down
34 changes: 24 additions & 10 deletions frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
class="main-menu-sidenav"
>
<mat-toolbar>Rocketadmin</mat-toolbar>
<mat-nav-list *ngIf="userLoggedIn === true">
<a mat-list-item routerLink="/connections-list"
<mat-nav-list *ngIf="userLoggedIn === true" (click)="drawer.close()">
<a *ngIf="!connectionID" mat-list-item routerLink="/connections-list"
routerLinkActive="nav-bar__button_active"
aria-label="List of connections">
<mat-icon matListItemIcon class="connection-navigation__icon">
Expand All @@ -21,7 +21,7 @@
{{navigationTabs[tab].caption}}
</a>
</mat-nav-list>
<a mat-list-item routerLink="/user-settings" class="connection-navigation__item_user" data-testid="account-link-account-menu">
<a mat-list-item routerLink="/user-settings" class="connection-navigation__item_user account-section-item" data-testid="account-link-account-menu">
<mat-icon matListItemIcon class="connection-navigation__icon connection-navigation__icon_account"
matBadge="1" [matBadgeHidden]="currentUser.isActive"
matBadgeColor="accent" matBadgeSize="small">
Expand All @@ -30,35 +30,44 @@
<div matListItemTitle>Account</div>
<div matListItemLine>{{currentUser.email}}</div>
</a>
<a mat-list-item routerLink="/company" data-testid="company-link-account-menu">
<a mat-list-item routerLink="/company" class="account-section-item" data-testid="company-link-account-menu">
<mat-icon matListItemIcon class="connection-navigation__icon">
apartment
</mat-icon>
<div matListItemTitle>Company</div>
</a>
<a mat-list-item routerLink="/secrets" data-testid="secrets-link-account-menu">
<a mat-list-item routerLink="/hosted-databases"
routerLinkActive="nav-bar__button_active"
aria-label="Hosted databases">
<mat-icon matListItemIcon fontSet="material-symbols-outlined" class="connection-navigation__icon">
database
</mat-icon>
<div matListItemTitle>Hosted databases</div>
</a>
Comment on lines +39 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Finish wiring the new Hosted databases entry across both account menus.

This adds the route in the mobile sidenav, but the desktop #accountMenu in this same template still jumps from Company straight to Secrets, so desktop users never see the new destination. Also, this anchor is the only new account-row item here without account-section-item, which means the compact sizing added in app.component.css will not apply to it.

🤖 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` around lines 39 - 46, The desktop
account menu (`#accountMenu`) is missing the new Hosted databases router entry and
the anchor lacks the account-section-item class; add an <a> element with
routerLink="/hosted-databases" and routerLinkActive="nav-bar__button_active"
into the `#accountMenu` account rows (placing it between the Company and Secrets
items to match mobile sidenav order) and ensure the anchor includes the
account-section-item class so the compact sizing in app.component.css applies;
update the existing anchor (the one already added to the mobile sidenav) or
duplicate its attributes (including mat-list-item, aria-label="Hosted
databases", mat-icon and matListItemTitle) into the `#accountMenu` markup.

<a mat-list-item routerLink="/secrets" class="account-section-item" data-testid="secrets-link-account-menu">
<mat-icon matListItemIcon class="connection-navigation__icon">
key
</mat-icon>
<div matListItemTitle>Secrets</div>
</a>
<a mat-list-item *ngIf="isSaas" routerLink="/zapier" data-testid="zapier-link-account-menu">
<a mat-list-item *ngIf="isSaas" routerLink="/zapier" class="account-section-item" data-testid="zapier-link-account-menu">
<mat-icon matListItemIcon class="connection-navigation__icon">
electric_bolt
</mat-icon>
<div matListItemTitle>Zapier</div>
</a>
<a mat-list-item href="https://docs.rocketadmin.com/" target="_blank">
<a mat-list-item class="account-section-item" href="https://docs.rocketadmin.com/" target="_blank">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add rel="noopener noreferrer" to the external Help center link.

Opening a new tab with target="_blank" without rel leaves window.opener exposed unnecessarily.

Suggested fix
-            <a mat-list-item class="account-section-item" href="https://docs.rocketadmin.com/" target="_blank">
+            <a
+                mat-list-item
+                class="account-section-item"
+                href="https://docs.rocketadmin.com/"
+                target="_blank"
+                rel="noopener noreferrer">
📝 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.

Suggested change
<a mat-list-item class="account-section-item" href="https://docs.rocketadmin.com/" target="_blank">
<a
mat-list-item
class="account-section-item"
href="https://docs.rocketadmin.com/"
target="_blank"
rel="noopener noreferrer">
🤖 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 59, The external Help center
anchor element with class "account-section-item" that uses target="_blank"
should include rel="noopener noreferrer" to prevent exposing window.opener;
update the <a> element (the anchor with href="https://docs.rocketadmin.com/" and
class "account-section-item") to add rel="noopener noreferrer" alongside the
existing target attribute.

<mat-icon matListItemIcon class="connection-navigation__icon" fontSet="material-symbols-outlined">help</mat-icon>
<div matListItemTitle>Help center</div>
</a>
<mat-list-item (click)="logOut()" data-testid="logout-button-account-menu">
<mat-list-item class="account-section-item" (click)="logOut()" data-testid="logout-button-account-menu">
<mat-icon matListItemIcon class="connection-navigation__icon">logout</mat-icon>
<div matListItemTitle>Log out</div>
</mat-list-item>
Comment on lines +63 to 66

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show the referenced region with line numbers
sed -n '40,90p' frontend/src/app/app.component.html | nl -ba | sed -n '1,120p'

# 2) Search for the logout handler usage
rg -n "logOut\\(" frontend/src/app/app.component.html frontend/src/app -S || true

# 3) Find other mat-list-item + click patterns for keyboard/a11y precedents
rg -n "mat-list-item\\b[^\\n]*\\(click\\)" frontend/src/app -S || true

# 4) Check whether mat-list-item is used as a button anywhere else
rg -n "<button[^>]*\\bmat-list-item\\b|\\bmat-list-item\\b[^\\n]*routerLink|mat-list-item\\b[^\\n]*href" frontend/src/app -S || true

Repository: rocket-admin/rocketadmin

Length of output: 111


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Show referenced region with line numbers (avoid nl)
sed -n '55,80p' frontend/src/app/app.component.html | cat -n

# 2) Search for the logout handler usage
rg -n "logOut\\(" frontend/src/app/app.component.html frontend/src/app -S || true

# 3) Find mat-list-item + click patterns for accessibility precedent
rg -n "mat-list-item\\b[^\\n]*\\(click\\)" frontend/src/app -S || true

# 4) Check for any existing pattern of using a real button with mat-list-item in this repo
rg -n "<button[^>]*\\bmat-list-item\\b|mat-list-item\\b[^\\n]*\\brole=\\\"button\\\"|mat-list-item\\b[^\\n]*\\btabindex=" frontend/src/app -S || true

Repository: rocket-admin/rocketadmin

Length of output: 3280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# A) Show the logout list-item region precisely
sed -n '55,75p' frontend/src/app/app.component.html | cat -n

# B) Show the other logout button usage near line ~222
sed -n '205,235p' frontend/src/app/app.component.html | cat -n

# C) Find all mat-list-item elements with (click)=
rg -n "mat-list-item\\b[^>]*\\(click\\)=" frontend/src/app -S || true

# D) Find whether repo already uses <button ... mat-list-item ...>
rg -n "<button[^>]*\\bmat-list-item\\b" frontend/src/app -S || true

# E) Find other mat-list-item usages to see intended pattern (a vs button vs bare mat-list-item)
rg -n "mat-list-item\\b" frontend/src/app -S | head -n 80

Repository: rocket-admin/rocketadmin

Length of output: 8002


Use a real button for the “Log out” row (account menu)

In frontend/src/app/app.component.html, the logout entry uses (click) on a bare <mat-list-item>, which doesn’t provide native button semantics or keyboard interaction. The repo already uses real buttons with mat-list-item elsewhere, and this page already uses a real <button> for the logout inside the mat-menu.

<mat-list-item class="account-section-item" (click)="logOut()" data-testid="logout-button-account-menu">
    <mat-icon matListItemIcon class="connection-navigation__icon">logout</mat-icon>
    <div matListItemTitle>Log out</div>
</mat-list-item>
Suggested fix
-            <mat-list-item class="account-section-item" (click)="logOut()" data-testid="logout-button-account-menu">
+            <button type="button" mat-list-item class="account-section-item" (click)="logOut()" data-testid="logout-button-account-menu">
                 <mat-icon matListItemIcon class="connection-navigation__icon">logout</mat-icon>
                 <div matListItemTitle>Log out</div>
-            </mat-list-item>
+            </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/app.component.html` around lines 63 - 66, Replace the
clickable plain mat-list-item with a real button element to restore native
button semantics and keyboard interaction: locate the logout entry using the
account-section-item class and data-testid "logout-button-account-menu" and
change it to a button with the mat-list-item attribute, keep the existing
(click)="logOut()" handler and the child mat-icon and title, and ensure the
button has type="button" and any existing testid/aria attributes preserved so
behavior and tests remain the same.

</mat-nav-list>
<a mat-flat-button color="accent" *ngIf="isSaas && currentUser && currentUser.role === 'ADMIN'" class="connection-navigation__upgrade-button" routerLink="/upgrade"
routerLinkActive="nav-bar__button_active">
routerLinkActive="nav-bar__button_active"
(click)="drawer.close()">
Upgrade
</a>
</mat-sidenav>
Expand Down Expand Up @@ -88,7 +97,7 @@
class="logo__image">
<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">
<img src="../assets/rocketadmin_logo_white.svg" alt="Rocketadmin logo" class="logo__image">
</picture>
</ng-template>
Expand Down Expand Up @@ -125,6 +134,11 @@
[ngClass]="{'connection_active': connectionID === connection.connection.id}">
{{connection.displayTitle}}
</a>
<mat-divider></mat-divider>
<a mat-menu-item routerLink="/hosted-databases">
<mat-icon class="nav-menu__list-link-icon" fontSet="material-symbols-outlined">database</mat-icon>
<span>Hosted databases</span>
</a>
</ng-template>
</mat-menu>
<span *ngIf="isDemo" class="logo__demo-mark">demo</span>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
import { ChangeDetectorRef, Component } from '@angular/core';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
Expand Down Expand Up @@ -50,6 +51,7 @@ amplitude.getInstance().init('9afd282be91f94da735c11418d5ff4f5');
MatButtonModule,
MatBadgeModule,
MatMenuModule,
MatDividerModule,
MatTooltipModule,
Angulartics2OnModule,
FeatureNotificationComponent,
Expand Down
Loading
Loading