Skip to content

Commit

Permalink
feat: finish gdpr contract list
Browse files Browse the repository at this point in the history
Signed-off-by: Dafnik <[email protected]>
  • Loading branch information
Dafnik committed Oct 31, 2024
1 parent b6c7b20 commit bf660d8
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {injectParams} from 'ngxtension/inject-params';

import {AppBackButtonComponent} from '@home-shared/components/button/app-back-button.component';
import {ScrollableToolbarComponent} from '@home-shared/components/scrollable-toolbar.component';
import {base64ToArrayBuffer} from '@home-shared/services/file.utils';

import {injectAPI} from '@shared/api';
import {AppProgressBarComponent} from '@shared/ui/loading/app-progress-bar.component';
Expand Down Expand Up @@ -175,21 +176,3 @@ export class TmpNotificationViewComponent {
cl_copy(it);
}
}

function base64ToArrayBuffer(base64: string): Uint8Array | undefined {
try {
if (!/^(?:[A-Za-z0-9+/]{4})*?(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(base64)) {
return undefined;
}

const binary_string = window.atob(base64);
const len = binary_string.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
} catch {
return undefined;
}
}
21 changes: 16 additions & 5 deletions src/app/home/_shared/services/file.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ export async function base64ToFileAndDownload(base64: string, fileName: string,
}

/**
* Converts a Base64 string to a Blob.
* Converts a Base64 string to an array buffer.
*
* @param base64 - The Base64-encoded string.
* @param mimeType - The MIME type of the data.
* @returns A Blob representing the binary data.
* @returns A ByteArray representing the binary data.
*/
export function base64ToBlob(base64: string, mimeType: string): Blob {
export function base64ToArrayBuffer(base64: string): Uint8Array {
// Decode the Base64 string
const byteCharacters: string = atob(base64);

Expand All @@ -41,7 +40,19 @@ export function base64ToBlob(base64: string, mimeType: string): Blob {
}

// Convert byte numbers to a Uint8Array
const byteArray: Uint8Array = new Uint8Array(byteNumbers);
return new Uint8Array(byteNumbers);
}

/**
* Converts a Base64 string to a Blob.
*
* @param base64 - The Base64-encoded string.
* @param mimeType - The MIME type of the data.
* @returns A Blob representing the binary data.
*/
export function base64ToBlob(base64: string, mimeType: string): Blob {
// Convert byte numbers to a Uint8Array
const byteArray = base64ToArrayBuffer(base64);

// Create and return the Blob
return new Blob([byteArray], {type: mimeType});
Expand Down
17 changes: 14 additions & 3 deletions src/app/home/settings/_services/gdpr.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import {inject} from '@angular/core';

import {pipe, switchMap, tap} from 'rxjs';

import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {tapResponse} from '@ngrx/operators';
import {patchState, signalStore, withMethods, withState} from '@ngrx/signals';
import {rxMethod} from '@ngrx/signals/rxjs-interop';

import {base64ToFileAndDownload} from '@home-shared/services/file.utils';
import {base64ToArrayBuffer} from '@home-shared/services/file.utils';

import {BackendType} from '@shared/api';

import {GDPRConfirmationModal} from '../gdpr/gdpr-confirmation-modal';
import {GDPRClient} from './gdpr.client';

type OrganisationsGDPRState = {
Expand All @@ -29,7 +31,7 @@ const initialState: OrganisationsGDPRState = {
export const GDPRStore = signalStore(
{providedIn: 'root'},
withState(initialState),
withMethods((store, gdprClient = inject(GDPRClient)) => ({
withMethods((store, gdprClient = inject(GDPRClient), modal = inject(NgbModal)) => ({
confirm: rxMethod<void>(
pipe(
tap(() => patchState(store, () => ({isLoading: true}))),
Expand Down Expand Up @@ -89,7 +91,16 @@ export const GDPRStore = signalStore(
switchMap((id) =>
gdprClient.loadAgreement(id).pipe(
tapResponse({
next: (agreement) => void base64ToFileAndDownload(agreement.base64Data, 'gdpr.pdf'),
next: (agreement) => {
const modalRef = modal.open(GDPRConfirmationModal, {
ariaLabelledBy: 'modal-gdpr-confirmation',
size: 'lg',
});

(modalRef.componentInstance as GDPRConfirmationModal).confirm.set(false);
(modalRef.componentInstance as GDPRConfirmationModal).contractDate.set(new Date(agreement.agreedOnAt));
(modalRef.componentInstance as GDPRConfirmationModal).pdf.set(base64ToArrayBuffer(agreement.base64Data));
},
error: () => {},
}),
),
Expand Down
46 changes: 46 additions & 0 deletions src/app/home/settings/gdpr/gdpr-confirmation-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {DatePipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';

import {TranslocoPipe} from '@jsverse/transloco';
import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import {PdfJsViewerModule} from 'ng2-pdfjs-viewer';

@Component({
template: `
<div class="modal-header">
<h4 class="modal-title" id="modal-gdpr-confirmation">
{{ 'HOME_ORGS_SETTINGS_GDPR_CONTRACT' | transloco }} - {{ contractDate() | date: 'dd.MM.YYYY' }}
</h4>
<button class="btn-close btn-close-white" (mousedown)="activeModal.close(undefined)" type="button" aria-label="Close"></button>
</div>
<div class="modal-body">
@if (pdf(); as pdf) {
<div style="height: 80vh; width: 100%">
<ng2-pdfjs-viewer [pdfSrc]="pdf" [viewBookmark]="false" />
</div>
}
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" (mousedown)="activeModal.close(undefined)" type="button">
{{ 'CLOSE' | transloco }}
</button>
@if (confirm()) {
<button class="btn btn-success" (mousedown)="activeModal.close(true)" type="submit">
{{ 'CONFIRM' | transloco }}
</button>
}
</div>
`,
selector: 'wr-gdpr-confirmation-modal',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TranslocoPipe, PdfJsViewerModule, DatePipe],
})
export class GDPRConfirmationModal {
activeModal = inject(NgbActiveModal);

confirm = signal(true);
contractDate = signal(new Date());

pdf = signal<Uint8Array | undefined>(undefined);
}
63 changes: 21 additions & 42 deletions src/app/home/settings/gdpr/gdpr-list.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import {DatePipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, effect, inject, viewChild} from '@angular/core';
import {Component, effect, inject, viewChild} from '@angular/core';

import {TranslocoPipe} from '@jsverse/transloco';
import {NgbActiveModal, NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {DfxSortModule, DfxTableModule, NgbSort} from 'dfx-bootstrap-table';

import {injectTable} from '@home-shared/list';
import {base64ToFileAndDownload} from '@home-shared/services/file.utils';
import {base64ToArrayBuffer} from '@home-shared/services/file.utils';

import {SelectedOrganisationService} from '@shared/services';
import {AppProgressBarComponent} from '@shared/ui/loading';

import {PrinterBatchUpdateDto} from '../../printers/printers-batch-update.modal';
import {GDPRStore} from '../_services/gdpr.store';
import {GDPRConfirmationModal} from './gdpr-confirmation-modal';

@Component({
template: `
<div class="d-flex flex-column gap-3">
<h1 class="my-0">{{ 'NAV_SETTINGS_GDPR' | transloco }}</h1>
<div>
<button class="btn btn-success" (click)="gdprStore.newAgreement()">Neuer Auftragsverarbeitungsvertrag</button>
<button class="btn btn-success" (click)="gdprStore.newAgreement()">{{ 'HOME_ORGS_SETTINGS_NEW_GDPR_CONTRACT' | transloco }}</button>
</div>
<div class="table-responsive">
<table [hover]="true" [dataSource]="table.dataSource()" ngb-table ngb-sort>
<ng-container ngbColumnDef="agreedAt">
<th *ngbHeaderCellDef ngb-header-cell ngb-sort-header>{{ 'Agreed at' | transloco }}</th>
<td *ngbCellDef="let agreement" ngb-cell>{{ agreement.agreedOnAt | date: 'YYYY.MM.dd HH:mm:ss' }}</td>
<th *ngbHeaderCellDef ngb-header-cell ngb-sort-header>{{ 'HOME_ORGS_SETTINGS_GDPR_AGREED_AT' | transloco }}</th>
<td *ngbCellDef="let agreement" ngb-cell>{{ agreement.agreedOnAt | date: 'dd.MM.YYYY HH:mm:ss' }}</td>
</ng-container>
<ng-container ngbColumnDef="agreedBy">
<th *ngbHeaderCellDef ngb-header-cell ngb-sort-header>{{ 'Agreed By' | transloco }}</th>
<th *ngbHeaderCellDef ngb-header-cell ngb-sort-header>{{ 'HOME_ORGS_SETTINGS_GDPR_AGREED_BY' | transloco }}</th>
<td *ngbCellDef="let agreement" ngb-cell>{{ agreement.agreedOnBy }}</td>
</ng-container>
Expand Down Expand Up @@ -63,24 +64,27 @@ export class GDPRList {
constructor() {
this.gdprStore.loadAll(this.#selectedOrganisationId$);

effect(() => {
const fileDto = this.gdprStore.fileDto();
if (fileDto) {
console.log('New gdpr file', fileDto);
effect(
() => {
const fileDto = this.gdprStore.fileDto();
if (fileDto) {
console.log('New gdpr file', fileDto);

void base64ToFileAndDownload(fileDto.data, 'gdpr.pdf');

this.openGDPRConfirmModal();
}
});
this.openGDPRConfirmModal(base64ToArrayBuffer(fileDto.data));
}
},
{allowSignalWrites: true},
);
}

openGDPRConfirmModal() {
openGDPRConfirmModal(pdf: Uint8Array) {
const modalRef = this.#modal.open(GDPRConfirmationModal, {
ariaLabelledBy: 'modal-gdpr-confirmation',
size: 'lg',
});

(modalRef.componentInstance as GDPRConfirmationModal).pdf.set(pdf);

void modalRef.result
.then((result?: PrinterBatchUpdateDto) => {
if (result) {
Expand All @@ -90,28 +94,3 @@ export class GDPRList {
.catch();
}
}

@Component({
template: `
<div class="modal-header">
<h4 class="modal-title" id="modal-gdpr-confirmation">Confirm</h4>
<button class="btn-close btn-close-white" (mousedown)="activeModal.close(undefined)" type="button" aria-label="Close"></button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" (mousedown)="activeModal.close(undefined)" type="button">
{{ 'CLOSE' | transloco }}
</button>
<button class="btn btn-success" (mousedown)="activeModal.close(true)" type="submit">
{{ 'CONFIRM' | transloco }}
</button>
</div>
`,
selector: 'wr-gdpr-confirmation-modal',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TranslocoPipe],
})
class GDPRConfirmationModal {
activeModal = inject(NgbActiveModal);
}
5 changes: 5 additions & 0 deletions src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"ORDER": "Anordnung",
"POSITION": "#",
"LEARN_MORE": "Erfahre mehr",
"CONFIRM": "Bestätigen",

"RECYCLE_BIN": "Papierkorb",
"RECYCLE_BIN_EMPTY": "Keine gelöschten Daten.",
Expand Down Expand Up @@ -184,6 +185,10 @@
"HOME_ORGS_SETTINGS_ACTIVATE_WAITER_ON_SIGN_IN_VIA_CREATE_TOKEN": "Personal bei Registrierung automatisch aktiveren",
"HOME_ORGS_SETTINGS_TIMEZONE": "Zeitzone",
"HOME_ORGS_SETTINGS_TIMEZONE_INVALID": "Ungültige Zeitzone",
"HOME_ORGS_SETTINGS_GDPR_CONTRACT": "Auftragsverarbeitungsvertrag",
"HOME_ORGS_SETTINGS_NEW_GDPR_CONTRACT": "Neuer Auftragsverarbeitungsvertrag",
"HOME_ORGS_SETTINGS_GDPR_AGREED_AT": "Zugestimmt am",
"HOME_ORGS_SETTINGS_GDPR_AGREED_BY": "Zugestimmt von",

"HOME_EVENTS_UPDATE_CREATE_WAITER_TOKEN": "Personal-Erstellungs QR-Code zurücksetzen",
"HOME_EVENTS_START_DATE": "Start-Datum",
Expand Down

0 comments on commit bf660d8

Please sign in to comment.