Skip to content
Open
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
72 changes: 54 additions & 18 deletions oar-dmp/src/app/dmp-form/dmp-form.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, OnInit, ViewChild, afterNextRender } from '@angular/core';
import { ObservedValueOf, Subscription } from "rxjs";
import { ObservedValueOf, Subscription, forkJoin, switchMap, of, EMPTY } from "rxjs";
import { UntypedFormBuilder } from '@angular/forms';
import { BasicInfoComponent } from '../form-components/basic-info/basic-info.component';
import { PersonelComponent } from '../form-components/personel/personel.component';
Expand Down Expand Up @@ -127,6 +127,10 @@ export class DmpFormComponent implements OnInit{
OUsUpdate: UpdateIndicator = {numUpdates:0, isUpdated:false};
OUsTotalUpdates:number = 0;

canWrite:boolean = false;
isAdmin:boolean = false;
canDelete:boolean = false;

constructor(
private fb: UntypedFormBuilder,
private dmp_Service: DmpService,
Expand All @@ -138,7 +142,7 @@ export class DmpFormComponent implements OnInit{
private updateOUs: UpdateNistContributorService

) {
// console.log("constructor");
console.log("constructor");
afterNextRender(() => {
// used for one-time initialisation:
// subscribe to track if the form has been changed by performing
Expand Down Expand Up @@ -185,7 +189,7 @@ export class DmpFormComponent implements OnInit{
getFromDB:boolean = false;

ngOnInit(): void {
// console.log("dmp-form.component ngOnInit")
console.log("dmp-form.component ngOnInit")

// const elementToObserve = document.getElementById("footer");

Expand All @@ -204,7 +208,8 @@ export class DmpFormComponent implements OnInit{

this.formButtonSubscribe();
this.formExportFormatSubscribe();
this.id = this.route.snapshot.paramMap.get('id')
this.id = this.route.snapshot.paramMap.get('id');

this.route.data.subscribe(data => {
this.action = data["action"] ;
if (this.action === "edit"){
Expand All @@ -217,24 +222,47 @@ export class DmpFormComponent implements OnInit{
}
});

// Fetch initial data from the backend
this.dmp_Service.fetchDMP(this.action, this.id).subscribe(
// 1. Start with the permission check
this.dmp_Service.aclsPermission(this.id, 'read').pipe(
switchMap(hasReadAccess => {
if (hasReadAccess) {
// 2. If true, move to the next "link" in the chain: fetch everything else
return forkJoin({
dmpData: this.dmp_Service.fetchDMP(this.action, this.id), // Fetch initial data from the backend
writePerm: this.dmp_Service.aclsPermission(this.id, 'write'),
adminPerm: this.dmp_Service.aclsPermission(this.id, 'admin'),
deletePerm: this.dmp_Service.aclsPermission(this.id, 'delete'),
});
}
else {
// 3. If false, stop the chain and handle the lack of access
this.router.navigate(['error', { dmpError: "You do not have read privileges for this record." }]);
return EMPTY; // Effectively kills the stream so 'next' isn't called
}
})
).subscribe(
{
next: data => {
next: (result) => {
const { dmpData, writePerm, adminPerm, deletePerm } = result;
if (this.id !==null){
// fetch DMP data from the backend
this.initialDMP = data.data;
this.dmp = data.data;
this.name.setValue(data.name);
this.initialDMP = dmpData.data;
this.dmp = dmpData.data;
this.name.setValue(dmpData.name);
this.getFromDB = true;
}
else{
// empty new form for creating new DMP
this.initialDMP = data;
this.dmp = data;
this.initialDMP = dmpData;
this.dmp = dmpData;
// disable save button by default until user has made a change on the form
this.disableSaveButton();
}

// Set permission flags
this.canWrite = writePerm;
this.isAdmin = adminPerm;
this.canDelete = deletePerm
},
error: error => {
console.log(error.message);
Expand All @@ -260,7 +288,13 @@ export class DmpFormComponent implements OnInit{
this.resetDmp();
}
else if (this.formButtonMessage === "Save"){
this.saveDraft();
if (this.canWrite){
this.saveDraft();
}
else{
alert("Can not save changes to DMP because you don't have write privileges on this record.");
}

}
else if (this.formButtonMessage === "Download"){
if (this.dmpExportFormatType === ""){
Expand Down Expand Up @@ -290,14 +324,14 @@ export class DmpFormComponent implements OnInit{
if (!this.formContributorsSubscription) {
//subscribe if not already subscribed
this.formContributorsSubscription = this.updateContributor.updateNISTContrib$.subscribe({
next: (hasChanged:UpdateIndicator) => {
next: (hasChanged:UpdateIndicator) => {
if (hasChanged.isUpdated){
// change this flag to indicate that we need to display alert about auto update of NIST contributors metadata from people service
this.contributorsUpdate.isUpdated = hasChanged.isUpdated;
// update this counter to indicate how many contributors have been updated
this.contribTotalUpdates = hasChanged.numUpdates;
this.saveDraft();
}
}
}
});
}
Expand All @@ -309,7 +343,7 @@ export class DmpFormComponent implements OnInit{
if (!this.formOUsSubscription) {
//subscribe if not already subscribed
this.formOUsSubscription = this.updateOUs.updateOUs$.subscribe({
next: (hasChanged:UpdateIndicator) => {
next: (hasChanged:UpdateIndicator) => {
if (hasChanged.isUpdated){
// change this flag to indicate that we need to display alert about auto update of NIST contributors metadata from people service
this.OUsUpdate.isUpdated = hasChanged.isUpdated;
Expand Down Expand Up @@ -406,8 +440,10 @@ export class DmpFormComponent implements OnInit{
// arr1 = [...arr1, ...arr2];
// arr1 is now [0, 1, 2, 3, 4, 5]
this.dmp = { ...this.dmp, ...patch };
this.contributorsSubscribe();
this.OUsSubscribe();
if (this.canWrite){
this.contributorsSubscribe();
this.OUsSubscribe();
}
}

enableSaveButton(){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,12 @@
}
</mat-chip-grid>
<input
placeholder="Copy/Paste or manually type in Keywords/Phrases as comma or semicolon separated values e.g. a, b; c"
#pathInput
placeholder="Copy/Paste or manually type in path(s) / URL(s) as comma or semicolon separated values e.g. a, b; c"
[matChipInputFor]="reactiveChipGrid"
(matChipInputTokenEnd)="addReactivePathsURLs($event)"
(blur)="onBlur($event)"
(input)="onInputChange(pathInput.value)"
/>
</mat-form-field>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Component, Input, Output, ChangeDetectionStrategy, inject, signal } from '@angular/core';
import { Component, Input, Output, ChangeDetectionStrategy, signal, ViewChild, ElementRef } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { defer, map, of, startWith } from 'rxjs';

import { MatChipInputEvent } from '@angular/material/chips';
import { MatChipInputEvent, MatChipInput } from '@angular/material/chips';
import { ChipsSplitterService } from 'src/app/shared/chips-splitter.service';


Expand All @@ -18,6 +18,12 @@ export class DataPreservationComponent {
separatorExp: RegExp = /,|;/;

reactivePathsURLs = signal(['']);
pathsInputVal = '';
// Reference the HTML input element that uses chips matching the #pathInput in the HTML
@ViewChild('pathInput') chipInputEl!: ElementRef<HTMLInputElement>;

// This finds the MatChipInput directive inside that same element
@ViewChild(MatChipInput) chipInputDirective!: MatChipInput;

preservationForm = this.fb.group(
{
Expand Down Expand Up @@ -145,11 +151,18 @@ export class DataPreservationComponent {
}

addReactivePathsURLs(event: MatChipInputEvent): void {
const chips = (this.spChips.splitChips(event.value.trim()) || '')
// To clean up chips array and ensure no empty strings or "just whitespace" items make it through,
// we should make fall back to an empty array [] and use the JavaScript .filter() method.
const chips = (this.spChips.splitChips(event.value.trim()) || [])
.filter(chip => chip.trim().length > 0);

// Add our keyword
// Add our path
if (chips) {
this.reactivePathsURLs.update(pathsURLs => [...pathsURLs, ...chips]);
this.reactivePathsURLs.update(pathsURLs => {
// Combine both arrays into a Set to force uniqueness,
// then spread it back into a standard array.
return [...new Set([...pathsURLs, ...chips])];
});
chips.forEach((chip)=>{
this.preservationForm.patchValue({
pathsURLs: chip
Expand All @@ -162,4 +175,34 @@ export class DataPreservationComponent {
event.chipInput!.clear();
}

onBlur(event: FocusEvent) {
// this is called if user did not hit enter on keyboard to add chips but has rather pressed
// elsewhere with a mouse

// Trigger event if input is not empty
if (this.pathsInputVal !== ''){
this.triggerAddChip();
}
}

onInputChange(value: string){
// console.log('onInputChange', value);
this.pathsInputVal = value;

}

triggerAddChip() {
//Construct the mock event
const mockEvent: MatChipInputEvent = {
input: this.chipInputEl.nativeElement,
value: this.pathsInputVal.trim(),
chipInput: this.chipInputDirective
};

// Manually call your existing add function
this.addReactivePathsURLs(mockEvent);
// Clear input value
this.pathsInputVal = '';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@
}
</mat-chip-grid>
<input
#chipInput
placeholder="Copy/Paste or manually type in Keywords/Phrases as comma or semicolon separated values e.g. a, b; c"

[matChipInputFor]="reactiveChipGrid"
(matChipInputTokenEnd)="addReactiveKeyword($event)"
(blur)="onBlur($event)"
(input)="onInputChange(chipInput.value)"
/>
</mat-form-field>
</div>
Expand Down
53 changes: 48 additions & 5 deletions oar-dmp/src/app/form-components/keywords/keywords.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Input, Output } from '@angular/core';
import { Component, Input, Output, ViewChild, ElementRef } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { MatChipInputEvent } from '@angular/material/chips';
import { MatChipInputEvent, MatChipInput} from '@angular/material/chips';
import { defer, map, of, startWith } from 'rxjs';
import { DMP_Meta } from '../../types/DMP.types';
import { ChipsSplitterService } from 'src/app/shared/chips-splitter.service';
Expand All @@ -21,7 +21,13 @@ export class KeywordsComponent {
}
);

reactiveKeywords = signal(['']);
reactiveKeywords = signal(['']);
keywordsInputVal = '';
// Reference the HTML input element that uses chips matching the #chipInput in the HTML
@ViewChild('chipInput') chipInputEl!: ElementRef<HTMLInputElement>;

// This finds the MatChipInput directive inside that same element
@ViewChild(MatChipInput) chipInputDirective!: MatChipInput;

constructor(private fb: UntypedFormBuilder, private spChips: ChipsSplitterService) {
// console.log("Keywords Component");
Expand Down Expand Up @@ -114,11 +120,18 @@ export class KeywordsComponent {
}

addReactiveKeyword(event: MatChipInputEvent): void {
const chips = (this.spChips.splitChips(event.value.trim()) || '')
// To clean up chips array and ensure no empty strings or "just whitespace" items make it through,
// we should make fall back to an empty array [] and use the JavaScript .filter() method.
const chips = (this.spChips.splitChips(event.value.trim()) || [])
.filter(chip => chip.trim().length > 0);

// Add our keyword
if (chips) {
this.reactiveKeywords.update(keywords => [...keywords, ...chips]);
this.reactiveKeywords.update(keywords => {
// Combine both arrays into a Set to force uniqueness,
// then spread it back into a standard array.
return [...new Set([...keywords, ...chips])];
});
chips.forEach((chip)=>{
this.keyWordsForm.patchValue({
keywords: chip
Expand All @@ -130,6 +143,36 @@ export class KeywordsComponent {
event.chipInput!.clear();
}

onBlur(event: FocusEvent) {
// this is called if user did not hit enter on keyboard to add chips but has rather pressed
// elsewhere with a mouse

// Trigger event if input is not empty
if (this.keywordsInputVal !== ''){
this.triggerAddChip();
}
}

onInputChange(value: string){
// console.log('onInputChange', value);
this.keywordsInputVal = value;

}

triggerAddChip() {
//Construct the mock event
const mockEvent: MatChipInputEvent = {
input: this.chipInputEl.nativeElement,
value: this.keywordsInputVal.trim(),
chipInput: this.chipInputDirective
};

// Manually call your existing add function
this.addReactiveKeyword(mockEvent);
// Clear input value
this.keywordsInputVal = '';
}



}
Loading