Skip to content

Commit

Permalink
Changes for the dummy event
Browse files Browse the repository at this point in the history
  • Loading branch information
cyprain-okeke committed Feb 13, 2025
1 parent e45ef6b commit ba7d7a3
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<app-header></app-header>

<app-header [isFreeOrFamilyOrg]="organization.isFreeOrFamilyOrg"></app-header>
<div class="tw-text-muted tw-mb-4" *ngIf="organization.isFreeOrFamilyOrg">
{{ dummyEventsDisclaimer }}
</div>
<div class="tw-mb-4" [formGroup]="eventsForm">
<div class="tw-mt-4 tw-flex tw-items-center">
<bit-form-field>
Expand Down Expand Up @@ -31,6 +33,7 @@
bitFormButton
buttonType="primary"
[bitAction]="refreshEvents"
[disabled]="organization.isFreeOrFamilyOrg"
>
{{ "update" | i18n }}
</button>
Expand All @@ -42,7 +45,7 @@
bitButton
bitFormButton
[bitAction]="exportEvents"
[disabled]="dirtyDates"
[disabled]="dirtyDates || organization.isFreeOrFamilyOrg"
>
<span>{{ "export" | i18n }}</span>
<i class="bwi bwi-fw bwi-sign-in" aria-hidden="true"></i>
Expand All @@ -58,7 +61,7 @@
></i>
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container>
<ng-container *ngIf="loaded">
<ng-container *ngIf="loaded && !organization.isFreeOrFamilyOrg">
<p *ngIf="!events || !events.length">{{ "noEventsInList" | i18n }}</p>
<bit-table *ngIf="events && events.length">
<ng-container header>
Expand Down Expand Up @@ -92,3 +95,55 @@
{{ "loadMore" | i18n }}
</button>
</ng-container>

<ng-container *ngIf="loaded && organization.isFreeOrFamilyOrg">
<bit-table>
<ng-container header>
<tr>
<th bitCell>{{ "timestamp" | i18n }}</th>
<th bitCell>{{ "client" | i18n }}</th>
<th bitCell>{{ "member" | i18n }}</th>
<th bitCell>{{ "event" | i18n }}</th>
</tr>
</ng-container>

<ng-template body>
<tr bitRow *ngFor="let event of dummyEventsSorted" alignContent="top">
<td bitCell class="tw-whitespace-nowrap">{{ event.timeStamp }}</td>
<td bitCell>
<span title="{{ event.deviceType }}">{{ event.deviceType }}</span>
</td>
<td bitCell>
<span title="{{ event.member }}">{{ event.member }}</span>
</td>
<td bitCell [innerHTML]="formatEventText(event.event)">{{ event.event }}</td>
</tr>

<tr bitRow *ngFor="let _ of [].constructor(6)" alignContent="top">
<td bitCell>******</td>
<td bitCell>***</td>
<td bitCell>**********</td>
<td bitCell>**********</td>
</tr>
</ng-template>
</bit-table>

<div
class="tw-relative tw--top-72 tw-bg-[#ffffff] tw-bg-opacity-90 tw-pb-5 tw-flex tw-items-center tw-justify-center"
>
<div
class="tw-bg-[#ffffff] tw-max-w-xl tw-flex-col tw-justify-center tw-text-center tw-p-5 tw-px-10 tw-rounded tw-border-0 tw-border-b tw-border-secondary-300 tw-border-solid mt-5"
>
<i class="bwi bwi-2x bwi-business text-primary"></i>

<p class="tw-font-bold mt-2">{{ "limitedEventLogs" | i18n: getOrganizationPlan() }}</p>
<p>
{{ "upgradeForFullEvents" | i18n }}
</p>

<button type="button" class="tw-mt-1" bitButton buttonType="primary" (click)="changePlan()">
{{ "changeBillingPlan" | i18n }}
</button>
</div>
</div>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// @ts-strict-ignore
import { Component, OnDestroy, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
import { concatMap, firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs";

import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import {
getOrganizationById,
OrganizationService,
Expand All @@ -15,14 +16,20 @@ import { ProviderService } from "@bitwarden/common/admin-console/abstractions/pr
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response";
import { EventSystemUser } from "@bitwarden/common/enums";
import { EventResponse } from "@bitwarden/common/models/response/event.response";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { DialogService, ToastService } from "@bitwarden/components";

import {
ChangePlanDialogResultType,
openChangePlanDialog,
} from "../../../billing/organizations/change-plan-dialog.component";
import { EventService } from "../../../core";
import { EventExportService } from "../../../tools/event-export";
import { BaseEventsComponent } from "../../common/base.events.component";
Expand All @@ -41,9 +48,52 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
exportFileName = "org-events";
organizationId: string;
organization: Organization;
sub: OrganizationSubscriptionResponse;
userOrg: Organization;
dummyEvents = [

Check warning on line 53 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L53

Added line #L53 was not covered by tests
{
timeStamp: this.getRandomDateTime(),
deviceType: "Extension - Firefox",
member: "Alice",
event: "Logged in",
},
{
timeStamp: this.getRandomDateTime(),
deviceType: "Mobile - iOS",
member: "Bob",
event: "Viewed item 000000",
},
{
timeStamp: this.getRandomDateTime(),
deviceType: "Desktop - Linux",
member: "Carlos",
event: "Login attempt failed with incorrect password",
},
{
timeStamp: this.getRandomDateTime(),
deviceType: "Web vault - Chrome",
member: "Ivan",
event: "Confirmed user 000000",
},
{
timeStamp: this.getRandomDateTime(),
deviceType: "Mobile - Android",
member: "Franz",
event: "Sent item 000000 to trash",
},
];

dummyEventsSorted: {
timeStamp: string;
deviceType: string;
member: string;
event: string;
}[];

private orgUsersUserIdMap = new Map<string, any>();
private destroy$ = new Subject<void>();
readonly dummyEventsDisclaimer =

Check warning on line 95 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L95

Added line #L95 was not covered by tests
"These events are examples only and do not reflect real events within your Bitwarden organization.";

constructor(
private apiService: ApiService,
Expand All @@ -57,10 +107,12 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
private userNamePipe: UserNamePipe,
private organizationService: OrganizationService,
private organizationUserApiService: OrganizationUserApiService,
private organizationApiService: OrganizationApiServiceAbstraction,
private providerService: ProviderService,
fileDownloadService: FileDownloadService,
toastService: ToastService,
private accountService: AccountService,
private dialogService: DialogService,

Check warning on line 115 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L115

Added line #L115 was not covered by tests
) {
super(
eventService,
Expand All @@ -84,10 +136,30 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
.organizations$(userId)
.pipe(getOrganizationById(this.organizationId)),
);
if (this.organization == null || !this.organization.useEvents) {

if (
!this.organization ||
(!this.organization.useEvents && !this.organization.isFreeOrFamilyOrg)
) {
//update true to isFamily
await this.router.navigate(["/organizations", this.organizationId]);
return;
}

if (this.organization.isOwner && this.organization.isFreeOrFamilyOrg) {
this.eventsForm.get("start").disable();
this.eventsForm.get("end").disable();

Check warning on line 151 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L150-L151

Added lines #L150 - L151 were not covered by tests

this.sortDummyEvents();

Check warning on line 153 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L153

Added line #L153 was not covered by tests

this.userOrg = await firstValueFrom(

Check warning on line 155 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L155

Added line #L155 was not covered by tests
this.organizationService
.organizations$(userId)
.pipe(getOrganizationById(this.organizationId)),
);
this.sub = await this.organizationApiService.getSubscription(this.organizationId);

Check warning on line 160 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L160

Added line #L160 was not covered by tests
}

await this.load();
}),
takeUntil(this.destroy$),
Expand Down Expand Up @@ -186,6 +258,57 @@ export class EventsComponent extends BaseEventsComponent implements OnInit, OnDe
return id?.substring(0, 8);
}

async changePlan() {
const reference = openChangePlanDialog(this.dialogService, {

Check warning on line 262 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L262

Added line #L262 was not covered by tests
data: {
organizationId: this.organizationId,
subscription: this.sub,
productTierType: this.userOrg.productTierType,
},
});

const result = await lastValueFrom(reference.closed);

Check warning on line 270 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L270

Added line #L270 was not covered by tests

if (result === ChangePlanDialogResultType.Closed) {
return;

Check warning on line 273 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L273

Added line #L273 was not covered by tests
}
await this.load();

Check warning on line 275 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L275

Added line #L275 was not covered by tests
}

getRandomDateTime() {
const now = new Date();
const past24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000);

Check warning on line 280 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L279-L280

Added lines #L279 - L280 were not covered by tests
const randomTime =
past24Hours.getTime() + Math.random() * (now.getTime() - past24Hours.getTime());
const randomDate = new Date(randomTime);

Check warning on line 283 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L282-L283

Added lines #L282 - L283 were not covered by tests

return randomDate.toLocaleString("en-US", {

Check warning on line 285 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L285

Added line #L285 was not covered by tests
month: "short",
day: "numeric",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: true,
});
}

sortDummyEvents() {
this.dummyEventsSorted = this.dummyEvents.sort(
(a, b) => new Date(a.timeStamp).getTime() - new Date(b.timeStamp).getTime(),

Check warning on line 298 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L297-L298

Added lines #L297 - L298 were not covered by tests
);
}

getOrganizationPlan() {
const tierValue = this.organization.productTierType;
const tierName = ProductTierType[tierValue];
return tierName;

Check warning on line 305 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L303-L305

Added lines #L303 - L305 were not covered by tests
}

formatEventText(event: string): string {
return event.replace(/000000/g, '<span class="tw-text-code">000000</span>');

Check warning on line 309 in apps/web/src/app/admin-console/organizations/manage/events.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/admin-console/organizations/manage/events.component.ts#L309

Added line #L309 was not covered by tests
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ const routes: Routes = [
{
path: "events",
component: EventsComponent,
canActivate: [organizationPermissionsGuard((org) => org.canAccessEventLogs)],
canActivate: [
organizationPermissionsGuard((org) => org.canAccessEventLogs || org.isFreeOrFamilyOrg),
],
data: {
titleId: "eventLogs",
},
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/app/layouts/header/web-header.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
>
<i *ngIf="icon" class="bwi {{ icon }}" aria-hidden="true"></i>
{{ title || (routeData.titleId | i18n) }}
<span bitBadge variant="danger" class="tw-mt-3" *ngIf="isFreeOrFamilyOrg">
{{ "upgrade" | i18n }}
</span>
<ng-content select="[slot=title-suffix]"></ng-content>
</h1>
</div>
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/app/layouts/header/web-header.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export class WebHeaderComponent {
*/
@Input() icon: string;

/** Whether the current organization is a free or family organization */
@Input() isFreeOrFamilyOrg = false;

Check warning on line 31 in apps/web/src/app/layouts/header/web-header.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/web/src/app/layouts/header/web-header.component.ts#L31

Added line #L31 was not covered by tests

protected routeData$: Observable<{ titleId: string }>;
protected account$: Observable<User & { id: UserId }>;
protected canLock$: Observable<boolean>;
Expand Down
12 changes: 12 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -10370,5 +10370,17 @@
},
"assignedExceedsAvailable": {
"message": "Assigned seats exceed available seats."
},
"limitedEventLogs": {
"message": "$PRODUCT_TYPE$ plans do not have access to real event logs",
"placeholders": {
"product_type": {
"content": "$1",
"example": "Teams"
}
}
},
"upgradeForFullEvents": {
"message": "Get full access to organization event logs by upgrading to a Teams or Enterprise plan."
}
}
10 changes: 9 additions & 1 deletion libs/common/src/admin-console/models/domain/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export class Organization {
}

get canAccessEventLogs() {
return (this.isAdmin || this.permissions.accessEventLogs) && this.useEvents;
return (

Check warning on line 175 in libs/common/src/admin-console/models/domain/organization.ts

View check run for this annotation

Codecov / codecov/patch

libs/common/src/admin-console/models/domain/organization.ts#L175

Added line #L175 was not covered by tests
((this.isAdmin || this.permissions.accessEventLogs) && this.useEvents) ||
(this.isOwner && this.isFreeOrFamilyOrg)
);
}

/**
Expand Down Expand Up @@ -349,6 +352,11 @@ export class Organization {
return !this.useTotp;
}

get isFreeOrFamilyOrg() {
// return true if organization is on free or family tier
return [ProductTierType.Free, ProductTierType.Families].includes(this.productTierType);

Check warning on line 357 in libs/common/src/admin-console/models/domain/organization.ts

View check run for this annotation

Codecov / codecov/patch

libs/common/src/admin-console/models/domain/organization.ts#L357

Added line #L357 was not covered by tests
}

get canManageSponsorships() {
return this.familySponsorshipAvailable || this.familySponsorshipFriendlyName !== null;
}
Expand Down

0 comments on commit ba7d7a3

Please sign in to comment.