-
-
Notifications
You must be signed in to change notification settings - Fork 18
mobile: widgets page, AI panel, sidebar polish + quick-filter chips #1822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8cedb77
0893e98
78617cf
f7d0979
37cf40e
84f5b7c
aa15566
6a3079e
bb8d0d7
bd40f6b
de3b4a2
c2a46f8
e9366ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 = [ | ||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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' -C3Repository: rocket-admin/rocketadmin Length of output: 5485 Ensure
🤖 Prompt for AI Agents |
||
|
|
||
| return { isSafe: true }; | ||
| } | ||
|
|
||
|
|
||
| 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"`); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,8 +75,8 @@ | |
| }, | ||
| { | ||
| "type": "anyComponentStyle", | ||
| "maximumWarning": "10kb", | ||
| "maximumError": "20kb" | ||
| "maximumWarning": "20kb", | ||
| "maximumError": "32kb" | ||
|
Comment on lines
+78
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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 20Repository: 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 20Repository: 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 30Repository: 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 -nrRepository: 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 20Repository: 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 20Repository: rocket-admin/rocketadmin Length of output: 1131 Investigate component style bloat before further relaxing
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 |
||
| } | ||
| ] | ||
| }, | ||
|
|
@@ -129,8 +129,8 @@ | |
| }, | ||
| { | ||
| "type": "anyComponentStyle", | ||
| "maximumWarning": "10kb", | ||
| "maximumError": "20kb" | ||
| "maximumWarning": "20kb", | ||
| "maximumError": "32kb" | ||
| } | ||
| ] | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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
fiRepository: 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
fiRepository: 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 -uRepository: 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 || trueRepository: 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 . || trueRepository: 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 || trueRepository: rocket-admin/rocketadmin Length of output: 5357 🌐 Web query:
💡 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
doneRepository: 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
fiRepository: rocket-admin/rocketadmin Length of output: 6299 Remove
🧰 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 |
||
|
|
||
| .main-menu-sidenav mat-toolbar { | ||
| display: none !important; | ||
| } | ||
| } | ||
|
|
||
| .nav-bar { | ||
| position: sticky; | ||
| top: 0; | ||
|
|
@@ -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; | ||
|
|
@@ -243,7 +269,7 @@ | |
| } | ||
|
|
||
| .connection-navigation__upgrade-button { | ||
| margin-top: 8px; | ||
| margin-top: -12px; | ||
| margin-left: 8px; | ||
| width: calc(100% - 16px); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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"> | ||||||||||||||||
|
|
@@ -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"> | ||||||||||||||||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Finish wiring the new Hosted databases entry across both account menus. This adds the route in the mobile sidenav, but the desktop 🤖 Prompt for AI Agents |
||||||||||||||||
| <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"> | ||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add Opening a new tab with 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| <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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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 || trueRepository: 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 || trueRepository: 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 80Repository: rocket-admin/rocketadmin Length of output: 8002 Use a real button for the “Log out” row (account menu) In <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 |
||||||||||||||||
| </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> | ||||||||||||||||
|
|
@@ -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> | ||||||||||||||||
|
|
@@ -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> | ||||||||||||||||
|
|
||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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:
timestamp without time zonetype is read as local date, not UTC (PostgreSQL) typeorm/typeorm#9627🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 189
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 2556
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 2376
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 2215
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 627
🏁 Script executed:
Repository: rocket-admin/rocketadmin
Length of output: 610
Use
timestamptzto avoid timezone-dependent comparison issues.createdAtis declared astimestamp(without time zone) but is compared downstream againstConstants.ONE_DAY_AGO()(a JSDate/UTC instant). If the database session timezone is not UTC, thecreatedAt >= :valid_aftercomparison 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. Usingtimestamptzensures the timestamp is stored and compared as UTC, making the comparison unambiguous. This applies to bothpassword-reset.entity.tsandemail-verification.entity.ts.🤖 Prompt for AI Agents