-
Notifications
You must be signed in to change notification settings - Fork 5
Feat/217 add new registries #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8dfcb80
80325c0
782ed30
a661107
ff1149b
610aa53
d5d30c9
f3a589a
fd429e4
14b72f5
fe74ebf
ad7feed
9d1df8d
aa43a92
8745263
f1e9da0
e1c044a
6963bcb
6292e9e
bee29ae
85e2ddd
a06e98a
ea7c4e3
dc40c7d
a9684e5
f677ef8
0c83d6f
0309f30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<section class="flex flex-column bg-white flex-1 h-full p-5 gap-4 w-full"> | ||
@if (currentPage()) { | ||
<h2>{{ currentPage().title }}</h2> | ||
|
||
@let questions = currentPage().questions || []; | ||
|
||
@if (currentPage().sections?.length) { | ||
@for (section of currentPage().sections; track section.id) { | ||
questions = section.questions; | ||
<p-card> | ||
<h3 class="mb-2">{{ section.title }}</h3> | ||
@if (section.description) { | ||
<p class="mb-3">{{ section.description }}</p> | ||
} | ||
<ng-container *ngTemplateOutlet="questionList"></ng-container> | ||
</p-card> | ||
} | ||
} @else { | ||
<ng-container *ngTemplateOutlet="questionList"></ng-container> | ||
} | ||
|
||
<ng-template #questionList> | ||
@for (question of questions; track question.id) { | ||
<p-card> | ||
<label [for]="question.groupKey"> | ||
<h3 class="mb-2"> | ||
{{ question.displayText }} | ||
@if (question.required) { | ||
<span class="text-red-500">*</span> | ||
} | ||
</h3> | ||
@if (question.helpText) { | ||
<p class="mb-3">{{ question.helpText }}</p> | ||
} | ||
@if (question.paragraphText) { | ||
<p class="mb-3">{{ question.paragraphText }}</p> | ||
} | ||
</label> | ||
@if (question.exampleText) { | ||
<p-inplace #inplaceRef styleClass="mb-4"> | ||
<ng-template #display> | ||
<span class="text-primary">{{ 'common.links.showExample' | translate }} </span> | ||
</ng-template> | ||
<ng-template #content> | ||
<div class="p-inplace-display"> | ||
<button | ||
class="text-primary border-none cursor-pointer bg-transparent text-sm" | ||
tabindex="0" | ||
role="button" | ||
(click)="inplaceRef.deactivate()" | ||
(keyup.enter)="inplaceRef.deactivate()" | ||
(keyup.space)="inplaceRef.deactivate()" | ||
> | ||
{{ 'common.links.hideExample' | translate }} | ||
</button> | ||
</div> | ||
<p class="m-0">{{ question.exampleText }}</p> | ||
</ng-template> | ||
</p-inplace> | ||
} | ||
|
||
@switch (question.fieldType) { | ||
@case (FieldType.TextArea) { | ||
<textarea id="{{ question.groupKey }}" class="w-full" rows="5" cols="30" pTextarea></textarea> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe here you need to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is already here |
||
} | ||
@case (FieldType.Radio) { | ||
<div class="flex flex-column gap-2"> | ||
@for (option of question.options; track option) { | ||
<div class="flex align -items-center gap-2"> | ||
<p-radioButton | ||
[inputId]="option.value" | ||
[(ngModel)]="radio" | ||
[name]="question.groupKey || ''" | ||
[value]="option.value" | ||
></p-radioButton> | ||
<label [for]="option.value" class="ml-2">{{ option.label }}</label> | ||
@if (option.helpText) { | ||
<osf-info-icon [tooltipText]="option.helpText"></osf-info-icon> | ||
} | ||
</div> | ||
} | ||
</div> | ||
} | ||
@case (FieldType.Checkbox) { | ||
<div class="flex flex-column gap-2"> | ||
@for (option of question.options; track option) { | ||
<div class="flex align-items-center gap-2"> | ||
<p-checkbox | ||
[inputId]="option.value" | ||
[name]="question.groupKey || ''" | ||
[value]="option.value" | ||
></p-checkbox> | ||
<label [for]="option.value" class="ml-2">{{ option.label }}</label> | ||
</div> | ||
} | ||
</div> | ||
} | ||
|
||
@case (FieldType.Text) { | ||
<input | ||
id="{{ question.groupKey }}" | ||
type="text" | ||
class="w-full" | ||
[placeholder]="question.exampleText" | ||
pInputText | ||
/> | ||
} | ||
@case (FieldType.File) { | ||
<h3 class="mb-2">Upload File</h3> | ||
<p class="mb-1">You may attach up to 5 file(s) to this question. Files cannot total over 5GB in size.</p> | ||
<p> | ||
Uploaded files will automatically be archived in this registration. They will also be added to a related | ||
project that will be created for this registration. | ||
</p> | ||
|
||
<p>File input is not implemented yet.</p> | ||
Comment on lines
+109
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Translation |
||
} | ||
} | ||
</p-card> | ||
} | ||
</ng-template> | ||
} | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { CustomStepComponent } from './custom-step.component'; | ||
|
||
describe('CustomStepComponent', () => { | ||
let component: CustomStepComponent; | ||
let fixture: ComponentFixture<CustomStepComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [CustomStepComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(CustomStepComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { select } from '@ngxs/store'; | ||
|
||
import { TranslatePipe } from '@ngx-translate/core'; | ||
|
||
import { Card } from 'primeng/card'; | ||
import { Checkbox } from 'primeng/checkbox'; | ||
import { Inplace } from 'primeng/inplace'; | ||
import { InputText } from 'primeng/inputtext'; | ||
import { RadioButton } from 'primeng/radiobutton'; | ||
import { Textarea } from 'primeng/textarea'; | ||
|
||
import { NgTemplateOutlet } from '@angular/common'; | ||
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core'; | ||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { ActivatedRoute } from '@angular/router'; | ||
|
||
import { InfoIconComponent } from '@osf/shared/components'; | ||
|
||
import { FieldType } from '../../enums'; | ||
import { RegistriesSelectors } from '../../store'; | ||
|
||
@Component({ | ||
selector: 'osf-custom-step', | ||
imports: [ | ||
Card, | ||
Textarea, | ||
RadioButton, | ||
FormsModule, | ||
Checkbox, | ||
|
||
InputText, | ||
NgTemplateOutlet, | ||
Inplace, | ||
TranslatePipe, | ||
InfoIconComponent, | ||
], | ||
templateUrl: './custom-step.component.html', | ||
styleUrl: './custom-step.component.scss', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
}) | ||
export class CustomStepComponent { | ||
private readonly route = inject(ActivatedRoute); | ||
step = signal(this.route.snapshot.params['step'].split('-')[0]); | ||
protected readonly pages = select(RegistriesSelectors.getPagesSchema); | ||
currentPage = computed(() => this.pages()[this.step() - 1]); | ||
protected readonly FieldType = FieldType; | ||
|
||
radio = null; | ||
|
||
constructor() { | ||
this.route.params.pipe(takeUntilDestroyed()).subscribe((params) => { | ||
this.step.set(+params['step'].split('-')[0]); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<osf-sub-header [title]="'registries.new.addNewRegistry' | translate" /> | ||
<osf-stepper | ||
class="block ml-4 mb-4" | ||
[steps]="steps()" | ||
[linear]="false" | ||
[currentStep]="currentStep()" | ||
(currentStepChange)="stepChange($event)" | ||
/> | ||
<router-outlet></router-outlet> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
:host { | ||
height: 100%; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { DraftsComponent } from './drafts.component'; | ||
|
||
describe('DraftsComponent', () => { | ||
let component: DraftsComponent; | ||
let fixture: ComponentFixture<DraftsComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [DraftsComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(DraftsComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { createDispatchMap, select } from '@ngxs/store'; | ||
|
||
import { TranslatePipe, TranslateService } from '@ngx-translate/core'; | ||
|
||
import { tap } from 'rxjs'; | ||
|
||
import { ChangeDetectionStrategy, Component, computed, effect, inject, Signal, signal } from '@angular/core'; | ||
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; | ||
|
||
import { StepperComponent, SubHeaderComponent } from '@osf/shared/components'; | ||
import { StepOption } from '@osf/shared/models'; | ||
import { LoaderService } from '@osf/shared/services'; | ||
|
||
import { defaultSteps } from '../../constants'; | ||
import { FetchDraft, FetchSchemaBlocks, RegistriesSelectors } from '../../store'; | ||
|
||
@Component({ | ||
selector: 'osf-drafts', | ||
imports: [RouterOutlet, StepperComponent, SubHeaderComponent, TranslatePipe], | ||
templateUrl: './drafts.component.html', | ||
styleUrl: './drafts.component.scss', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
providers: [TranslateService], | ||
}) | ||
export class DraftsComponent { | ||
private readonly router = inject(Router); | ||
private readonly route = inject(ActivatedRoute); | ||
private readonly loaderService = inject(LoaderService); | ||
private readonly translateService = inject(TranslateService); | ||
|
||
protected readonly pages = select(RegistriesSelectors.getPagesSchema); | ||
protected readonly draftRegistration = select(RegistriesSelectors.getDraftRegistration); | ||
|
||
private readonly actions = createDispatchMap({ | ||
getSchemaBlocks: FetchSchemaBlocks, | ||
getDraftRegistration: FetchDraft, | ||
}); | ||
|
||
get isReviewPage(): boolean { | ||
return this.router.url.includes('/review'); | ||
} | ||
|
||
defaultSteps: StepOption[] = defaultSteps.map((step) => ({ | ||
...step, | ||
label: this.translateService.instant(step.label), | ||
})); | ||
|
||
steps: Signal<StepOption[]> = computed(() => { | ||
const customSteps = this.pages().map((page) => ({ | ||
label: page.title, | ||
value: page.id, | ||
})); | ||
return [this.defaultSteps[0], ...customSteps, this.defaultSteps[1]]; | ||
}); | ||
|
||
currentStep = signal( | ||
this.route.snapshot.children[0]?.params['step'] ? +this.route.snapshot.children[0]?.params['step'].split('-')[0] : 0 | ||
); | ||
|
||
registrationId = this.route.snapshot.children[0]?.params['id'] || ''; | ||
|
||
constructor() { | ||
this.loaderService.show(); | ||
if (!this.draftRegistration()) { | ||
this.actions.getDraftRegistration(this.registrationId); | ||
} | ||
effect(() => { | ||
const registrationSchemaId = this.draftRegistration()?.registrationSchemaId; | ||
if (registrationSchemaId) { | ||
this.actions | ||
.getSchemaBlocks(registrationSchemaId || '') | ||
.pipe( | ||
tap(() => { | ||
this.loaderService.hide(); | ||
}) | ||
) | ||
.subscribe(); | ||
} | ||
}); | ||
|
||
effect(() => { | ||
const reviewStepNumber = this.pages().length + 1; | ||
if (this.isReviewPage) { | ||
this.currentStep.set(reviewStepNumber); | ||
} | ||
}); | ||
} | ||
|
||
stepChange(step: number): void { | ||
// [NM] TODO: before navigating, validate the current step | ||
this.currentStep.set(step); | ||
const pageStep = this.steps()[step]; | ||
|
||
let pageLink = ''; | ||
if (!pageStep.value) { | ||
pageLink = `${pageStep.routeLink}`; | ||
} else { | ||
pageLink = `${step}-${pageStep.value}`; | ||
} | ||
this.router.navigate([`/registries/drafts/${this.registrationId}/`, pageLink]); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<p-card class="w-full"> | ||
<h2 class="mb-2">{{ 'project.overview.metadata.contributors' | translate }}</h2> | ||
<osf-contributors-list | ||
(focusout)="onFocusOut()" | ||
class="w-full" | ||
[contributors]="contributors()" | ||
[isLoading]="isContributorsLoading()" | ||
(remove)="removeContributor($event)" | ||
(showEducationHistory)="openEducationHistory($event)" | ||
(showEmploymentHistory)="openEmploymentHistory($event)" | ||
></osf-contributors-list> | ||
<div class="flex justify-content-end mt-3"> | ||
@if (hasChanges) { | ||
<div class="flex gap-2 mr-4"> | ||
<p-button (click)="cancel()" text severity="info" [label]="'common.buttons.cancel' | translate"> </p-button> | ||
<p-button (click)="save()" [label]="'common.buttons.save' | translate"> </p-button> | ||
</div> | ||
} | ||
<p-button | ||
[label]="'registries.metadata.addContributors' | translate" | ||
type="submit" | ||
(click)="openAddContributorDialog()" | ||
></p-button> | ||
</div> | ||
</p-card> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To replace the default page list question with a page section questions if exist