Skip to content
Merged
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
2 changes: 1 addition & 1 deletion frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"stylePreprocessorOptions": {
"includePaths": ["node_modules/@brumeilde/ngx-theme/presets/material"]
},
"scripts": [],
"scripts": ["node_modules/mermaid/dist/mermaid.min.js"],
"extractLicenses": false,
"sourceMap": true,
"optimization": false,
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/app/components/company/company.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,52 @@
color: var(--color-successPalette-500);
}

.access-popup {
background: var(--mat-menu-container-color, #fff);
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
padding: 8px 12px;
min-width: 160px;
white-space: nowrap;
display: flex;
flex-direction: column;
font-family: var(--mat-menu-item-label-text-font, inherit);
font-size: var(--mat-menu-item-label-text-size, 14px);
}

.access-popup__connection {
display: flex;
flex-direction: column;
}

.access-popup__connection-title {
font-weight: 500;
}

.access-popup__connection-db {
font-size: 0.786em;
color: rgba(0, 0, 0, 0.45);
padding-left: 2px;
}

@media (prefers-color-scheme: dark) {
.access-popup__connection-db {
color: rgba(255, 255, 255, 0.45);
}
}

.access-popup__group {
font-size: 0.857em;
color: rgba(0, 0, 0, 0.6);
padding-left: 8px;
}

@media (prefers-color-scheme: dark) {
.access-popup__group {
color: rgba(255, 255, 255, 0.6);
}
}

.company-member-cell_not-accessed {
color: var(--color-warnPalette-500);
}
Expand Down
34 changes: 31 additions & 3 deletions frontend/src/app/components/company/company.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,42 @@ <h2 class="heading-2 tableHeader__heading">Members <span *ngIf="currentPlan ===
</td>
</ng-container>

<!-- Active Column -->
<!-- Access Column -->
<ng-container matColumnDef="access">
<th mat-header-cell *matHeaderCellDef matTooltip="Access to connection">Access</th>
<td mat-cell *matCellDef="let element; let i = index"
data-label="Access"
class="company-member-cell company-member-cell_content-center">
<mat-icon *ngIf="element.has_groups" class="company-member-cell_accessed" fontSet="material-symbols-outlined">check_circle</mat-icon>
<mat-icon *ngIf="!element.has_groups" class="company-member-cell_not-accessed" fontSet="material-symbols-outlined">cancel</mat-icon>
@if (element.has_groups) {
<mat-icon class="company-member-cell_accessed" fontSet="material-symbols-outlined"
#overlayOrigin="cdkOverlayOrigin"
cdkOverlayOrigin
(mouseenter)="element._popupOpen = true"
(mouseleave)="element._popupOpen = false">check_circle</mat-icon>
<ng-template
Comment on lines +209 to +214
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="overlayOrigin"
[cdkConnectedOverlayOpen]="element._popupOpen"
[cdkConnectedOverlayPositions]="[
{ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -4 },
{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 4 }
]"
[cdkConnectedOverlayHasBackdrop]="false">
<div class="access-popup" (mouseleave)="element._popupOpen = false">
Comment on lines +209 to +223
Comment on lines +212 to +223
@for (conn of element.user_membership; track conn.id) {
<div class="access-popup__connection">
<span class="access-popup__connection-title">{{ conn.title }}</span>
<span class="access-popup__connection-db">{{ conn.database }}</span>
@for (group of conn.groups; track group.id) {
<span class="access-popup__group">{{ group.title }}</span>
}
</div>
}
</div>
</ng-template>
} @else {
<mat-icon class="company-member-cell_not-accessed" fontSet="material-symbols-outlined">cancel</mat-icon>
}
Comment on lines +208 to +237
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

Make the access popup keyboard-accessible.

The popup is currently mouse-only (mouseenter/mouseleave), so keyboard users can’t open or inspect access details.

Suggested minimal fix
- <mat-icon class="company-member-cell_accessed" fontSet="material-symbols-outlined"
+ <mat-icon class="company-member-cell_accessed" fontSet="material-symbols-outlined"
+     tabindex="0"
+     role="button"
+     aria-label="Show member access details"
      `#overlayOrigin`="cdkOverlayOrigin"
      cdkOverlayOrigin
      (mouseenter)="element._popupOpen = true"
-     (mouseleave)="element._popupOpen = false">check_circle</mat-icon>
+     (mouseleave)="element._popupOpen = false"
+     (focus)="element._popupOpen = true"
+     (blur)="element._popupOpen = false">check_circle</mat-icon>

- <div class="access-popup" (mouseleave)="element._popupOpen = false">
+ <div class="access-popup"
+      (mouseenter)="element._popupOpen = true"
+      (mouseleave)="element._popupOpen = false">
📝 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
@if (element.has_groups) {
<mat-icon class="company-member-cell_accessed" fontSet="material-symbols-outlined"
#overlayOrigin="cdkOverlayOrigin"
cdkOverlayOrigin
(mouseenter)="element._popupOpen = true"
(mouseleave)="element._popupOpen = false">check_circle</mat-icon>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="overlayOrigin"
[cdkConnectedOverlayOpen]="element._popupOpen"
[cdkConnectedOverlayPositions]="[
{ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -4 },
{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 4 }
]"
[cdkConnectedOverlayHasBackdrop]="false">
<div class="access-popup" (mouseleave)="element._popupOpen = false">
@for (conn of element.user_membership; track conn.id) {
<div class="access-popup__connection">
<span class="access-popup__connection-title">{{ conn.title }}</span>
<span class="access-popup__connection-db">{{ conn.database }}</span>
@for (group of conn.groups; track group.id) {
<span class="access-popup__group">{{ group.title }}</span>
}
</div>
}
</div>
</ng-template>
} @else {
<mat-icon class="company-member-cell_not-accessed" fontSet="material-symbols-outlined">cancel</mat-icon>
}
`@if` (element.has_groups) {
<mat-icon class="company-member-cell_accessed" fontSet="material-symbols-outlined"
tabindex="0"
role="button"
aria-label="Show member access details"
`#overlayOrigin`="cdkOverlayOrigin"
cdkOverlayOrigin
(mouseenter)="element._popupOpen = true"
(mouseleave)="element._popupOpen = false"
(focus)="element._popupOpen = true"
(blur)="element._popupOpen = false">check_circle</mat-icon>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="overlayOrigin"
[cdkConnectedOverlayOpen]="element._popupOpen"
[cdkConnectedOverlayPositions]="[
{ originX: 'center', originY: 'top', overlayX: 'center', overlayY: 'bottom', offsetY: -4 },
{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top', offsetY: 4 }
]"
[cdkConnectedOverlayHasBackdrop]="false">
<div class="access-popup"
(mouseenter)="element._popupOpen = true"
(mouseleave)="element._popupOpen = false">
`@for` (conn of element.user_membership; track conn.id) {
<div class="access-popup__connection">
<span class="access-popup__connection-title">{{ conn.title }}</span>
<span class="access-popup__connection-db">{{ conn.database }}</span>
`@for` (group of conn.groups; track group.id) {
<span class="access-popup__group">{{ group.title }}</span>
}
</div>
}
</div>
</ng-template>
} `@else` {
<mat-icon class="company-member-cell_not-accessed" fontSet="material-symbols-outlined">cancel</mat-icon>
}
🤖 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/company/company.component.html` around lines 208
- 237, The access popup is only triggered by mouse events
(mouseenter/mouseleave) so add keyboard support by making the trigger icon
focusable (add tabindex) and handling focus/blur and keyboard events to toggle
element._popupOpen; specifically, update the mat-icon that uses overlayOrigin
and element._popupOpen to include tabindex and (focus)="element._popupOpen =
true", (blur)="element._popupOpen = false", and a (keydown) handler that opens
on Enter/Space and closes on Escape, and ensure the cdkConnectedOverlay has
appropriate aria attributes (aria-haspopup, aria-expanded bound to
element._popupOpen) so screen readers and keyboard users can open/close the
overlay created by cdkConnectedOverlay consistently.

</td>
</ng-container>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ describe('CompanyComponent', () => {
is_2fa_enabled: false,
role: CompanyMemberRole.Member,
has_groups: false,
user_membership: [],
};

component.handleDeleteMemberDialogOpen(fakeMember);
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/app/components/company/company.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NgIf } from '@angular/common';
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
Expand Down Expand Up @@ -37,6 +38,8 @@ import { RevokeInvitationDialogComponent } from './revoke-invitation-dialog/revo
styleUrls: ['./company.component.css'],
imports: [
NgIf,
CdkOverlayOrigin,
CdkConnectedOverlay,
FormsModule,
MatFormFieldModule,
MatInputModule,
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/app/models/company.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ export interface Company {
show_test_connections: boolean;
}

export interface CompanyMembershipGroup {
id: string;
title: string;
}

export interface CompanyMembershipConnection {
id: string;
title: string;
database: string;
groups: CompanyMembershipGroup[];
}

export interface CompanyMember {
id: string;
isActive: boolean;
Expand All @@ -42,6 +54,7 @@ export interface CompanyMember {
is_2fa_enabled: boolean;
role: CompanyMemberRole;
has_groups: boolean;
user_membership: CompanyMembershipConnection[];
}

export enum CompanyMemberRole {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { DynamicModule } from 'ng-dynamic-component';
import { SignalComponentIoModule } from 'ng-dynamic-component/signal-component-io';
import { provideCharts, withDefaultRegisterables } from 'ng2-charts';
import { CookieService } from 'ngx-cookie-service';
import { MarkdownModule, provideMarkdown } from 'ngx-markdown';
import { MERMAID_OPTIONS, MarkdownModule, provideMarkdown } from 'ngx-markdown';
import { NgxStripeModule } from 'ngx-stripe';
import { AppComponent } from './app/app.component';
import { AppRoutingModule } from './app/app-routing.module';
Expand Down Expand Up @@ -127,6 +127,7 @@ bootstrapApplication(AppComponent, {
CookieService,
provideMarkdown({
mermaidOptions: {
provide: MERMAID_OPTIONS,
useValue: {
startOnLoad: false,
theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/styles.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @import 'codemirror/lib/codemirror';
// @import 'codemirror/theme/ttcn';


* {
margin: 0;
padding: 0;
Expand Down
Loading