mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 00:33:44 +00:00
Move web to apps/web and bitwarden_license/bit-web
This commit is contained in:
30
apps/web/src/app/components/nested-checkbox.component.html
Normal file
30
apps/web/src/app/components/nested-checkbox.component.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[name]="pascalize(parentId)"
|
||||
[id]="parentId"
|
||||
[(ngModel)]="parentChecked"
|
||||
[indeterminate]="parentIndeterminate"
|
||||
/>
|
||||
<label class="form-check-label font-weight-normal" [for]="parentId">
|
||||
{{ parentId | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group form-group-child-check mb-0">
|
||||
<div class="form-check mt-1" *ngFor="let c of checkboxes">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[name]="pascalize(c.id)"
|
||||
[id]="c.id"
|
||||
[ngModel]="c.get()"
|
||||
(ngModelChange)="c.set($event)"
|
||||
/>
|
||||
<label class="form-check-label font-weight-normal" [for]="c.id">
|
||||
{{ c.id | i18n }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
32
apps/web/src/app/components/nested-checkbox.component.ts
Normal file
32
apps/web/src/app/components/nested-checkbox.component.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
|
||||
@Component({
|
||||
selector: "app-nested-checkbox",
|
||||
templateUrl: "nested-checkbox.component.html",
|
||||
})
|
||||
export class NestedCheckboxComponent {
|
||||
@Input() parentId: string;
|
||||
@Input() checkboxes: { id: string; get: () => boolean; set: (v: boolean) => void }[];
|
||||
@Output() onSavedUser = new EventEmitter();
|
||||
@Output() onDeletedUser = new EventEmitter();
|
||||
|
||||
get parentIndeterminate() {
|
||||
return !this.parentChecked && this.checkboxes.some((c) => c.get());
|
||||
}
|
||||
|
||||
get parentChecked() {
|
||||
return this.checkboxes.every((c) => c.get());
|
||||
}
|
||||
|
||||
set parentChecked(value: boolean) {
|
||||
this.checkboxes.forEach((c) => {
|
||||
c.set(value);
|
||||
});
|
||||
}
|
||||
|
||||
pascalize(s: string) {
|
||||
return Utils.camelToPascalCase(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<div *ngIf="loaded && activeOrganization != null" class="tw-flex">
|
||||
<button
|
||||
class="tw-flex tw-items-center tw-bg-background-alt tw-border-none"
|
||||
type="button"
|
||||
id="pickerButton"
|
||||
[appA11yTitle]="'organizationPicker' | i18n"
|
||||
[bitMenuTriggerFor]="orgPickerMenu"
|
||||
>
|
||||
<app-avatar
|
||||
[data]="activeOrganization.name"
|
||||
size="45"
|
||||
[circle]="true"
|
||||
[dynamic]="true"
|
||||
></app-avatar>
|
||||
<div class="tw-flex">
|
||||
<div class="org-name tw-ml-3">
|
||||
<span>{{ activeOrganization.name }}</span>
|
||||
<small class="tw-text-muted">{{ "organization" | i18n }}</small>
|
||||
</div>
|
||||
<div class="tw-ml-3">
|
||||
<i class="bwi bwi-angle-down tw-text-main" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div>
|
||||
<div
|
||||
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-danger-500 tw-text-danger"
|
||||
*ngIf="!activeOrganization.enabled"
|
||||
>
|
||||
<div class="tw-py-2 tw-px-5">
|
||||
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
|
||||
{{ "organizationIsDisabled" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tw-ml-3 tw-border tw-border-solid tw-rounded tw-border-info-500 tw-text-info"
|
||||
*ngIf="activeOrganization.isProviderUser"
|
||||
>
|
||||
<div class="tw-py-2 tw-px-5">
|
||||
<i class="bwi bwi-exclamation-triangle" aria-hidden="true"></i>
|
||||
{{ "accessingUsingProvider" | i18n: activeOrganization.providerName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<bit-menu #orgPickerMenu>
|
||||
<ul aria-labelledby="pickerButton" class="tw-p-0 tw-m-0">
|
||||
<li *ngFor="let org of organizations" class="tw-list-none tw-flex tw-flex-col" role="none">
|
||||
<a bitMenuItem [routerLink]="['/organizations', org.id]">
|
||||
<i
|
||||
class="bwi bwi-check mr-2"
|
||||
[ngClass]="org.id === activeOrganization.id ? 'visible' : 'invisible'"
|
||||
>
|
||||
<span class="tw-sr-only">{{ "currentOrganization" | i18n }}</span>
|
||||
</i>
|
||||
{{ org.name }}
|
||||
</a>
|
||||
</li>
|
||||
<bit-menu-divider></bit-menu-divider>
|
||||
<li class="tw-list-none" role="none">
|
||||
<a bitMenuItem routerLink="/create-organization">
|
||||
<i class="bwi bwi-plus mr-2"></i>
|
||||
{{ "newOrganization" | i18n }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</bit-menu>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { Organization } from "jslib-common/models/domain/organization";
|
||||
|
||||
import { NavigationPermissionsService } from "../organizations/services/navigation-permissions.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-organization-switcher",
|
||||
templateUrl: "organization-switcher.component.html",
|
||||
})
|
||||
export class OrganizationSwitcherComponent implements OnInit {
|
||||
constructor(private organizationService: OrganizationService, private i18nService: I18nService) {}
|
||||
|
||||
@Input() activeOrganization: Organization = null;
|
||||
organizations: Organization[] = [];
|
||||
|
||||
loaded = false;
|
||||
|
||||
async ngOnInit() {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
async load() {
|
||||
const orgs = await this.organizationService.getAll();
|
||||
this.organizations = orgs
|
||||
.filter((org) => NavigationPermissionsService.canAccessAdmin(org))
|
||||
.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
53
apps/web/src/app/components/password-reprompt.component.html
Normal file
53
apps/web/src/app/components/password-reprompt.component.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<div class="modal fade" role="dialog" aria-modal="true" aria-labelledby="confirmUserTitle">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="confirmUserTitle">
|
||||
{{ "passwordConfirmation" | i18n }}
|
||||
</h2>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ "passwordConfirmationDesc" | i18n }}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<div class="d-flex">
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
name="MasterPassword"
|
||||
class="text-monospace form-control"
|
||||
[(ngModel)]="masterPassword"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="ml-1 btn btn-link"
|
||||
appA11yTitle="{{ 'toggleVisibility' | i18n }}"
|
||||
(click)="togglePassword()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-eye': !showPassword, 'bwi-eye-slash': showPassword }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{ "ok" | i18n }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{ "cancel" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { PasswordRepromptComponent as BasePasswordRepromptComponent } from "jslib-angular/components/password-reprompt.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "password-reprompt.component.html",
|
||||
})
|
||||
export class PasswordRepromptComponent extends BasePasswordRepromptComponent {}
|
||||
14
apps/web/src/app/components/password-strength.component.html
Normal file
14
apps/web/src/app/components/password-strength.component.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<div class="progress">
|
||||
<div
|
||||
class="progress-bar {{ color }}"
|
||||
role="progressbar"
|
||||
[ngStyle]="{ width: scoreWidth + '%' }"
|
||||
attr.aria-valuenow="{{ scoreWidth }}"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
>
|
||||
<ng-container *ngIf="showText && text">
|
||||
{{ text }}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
40
apps/web/src/app/components/password-strength.component.ts
Normal file
40
apps/web/src/app/components/password-strength.component.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { Component, Input, OnChanges } from "@angular/core";
|
||||
|
||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-password-strength",
|
||||
templateUrl: "password-strength.component.html",
|
||||
})
|
||||
export class PasswordStrengthComponent implements OnChanges {
|
||||
@Input() score?: number;
|
||||
@Input() showText = false;
|
||||
|
||||
scoreWidth = 0;
|
||||
color = "bg-danger";
|
||||
text: string;
|
||||
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.scoreWidth = this.score == null ? 0 : (this.score + 1) * 20;
|
||||
switch (this.score) {
|
||||
case 4:
|
||||
this.color = "bg-success";
|
||||
this.text = this.i18nService.t("strong");
|
||||
break;
|
||||
case 3:
|
||||
this.color = "bg-primary";
|
||||
this.text = this.i18nService.t("good");
|
||||
break;
|
||||
case 2:
|
||||
this.color = "bg-warning";
|
||||
this.text = this.i18nService.t("weak");
|
||||
break;
|
||||
default:
|
||||
this.color = "bg-danger";
|
||||
this.text = this.score != null ? this.i18nService.t("weak") : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
apps/web/src/app/components/premium-badge.component.ts
Normal file
19
apps/web/src/app/components/premium-badge.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { MessagingService } from "jslib-common/abstractions/messaging.service";
|
||||
|
||||
@Component({
|
||||
selector: "app-premium-badge",
|
||||
template: `
|
||||
<button *appNotPremium bitBadge badgeType="success" (click)="premiumRequired()">
|
||||
{{ "premium" | i18n }}
|
||||
</button>
|
||||
`,
|
||||
})
|
||||
export class PremiumBadgeComponent {
|
||||
constructor(private messagingService: MessagingService) {}
|
||||
|
||||
premiumRequired() {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user