diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/Dmp.java b/dmp-backend/core/src/main/java/eu/eudat/model/Dmp.java index 907a35d0b..838110ab1 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/Dmp.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/Dmp.java @@ -95,6 +95,10 @@ public class Dmp { public static final String _dmpUsers = "dmpUsers"; + private List descriptions; + + public static final String _descriptions = "descriptions"; + public UUID getId() { return id; } @@ -262,4 +266,12 @@ public class Dmp { public void setVersionStatus(DmpVersionStatus versionStatus) { this.versionStatus = versionStatus; } + + public List getDescriptions() { + return descriptions; + } + + public void setDescriptions(List descriptions) { + this.descriptions = descriptions; + } } diff --git a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBuilder.java b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBuilder.java index f4434fcd7..f88bdc3da 100644 --- a/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBuilder.java +++ b/dmp-backend/core/src/main/java/eu/eudat/model/builder/DmpBuilder.java @@ -4,10 +4,7 @@ import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.convention.ConventionService; import eu.eudat.data.DmpEntity; import eu.eudat.model.*; -import eu.eudat.query.DmpBlueprintQuery; -import eu.eudat.query.DmpReferenceQuery; -import eu.eudat.query.DmpUserQuery; -import eu.eudat.query.UserQuery; +import eu.eudat.query.*; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.query.QueryFactory; import gr.cite.tools.exception.MyApplicationException; @@ -69,6 +66,9 @@ public class DmpBuilder extends BaseBuilder { FieldSet blueprintFields = fields.extractPrefixed(this.asPrefix(Dmp._blueprint)); Map blueprintItemsMap = this.collectDmpBlueprints(blueprintFields, data); + FieldSet descriptionsFields = fields.extractPrefixed(this.asPrefix(Dmp._descriptions)); + Map> descriptionsMap = this.collectDmpDescriptions(descriptionsFields, data); + for (DmpEntity d : data) { Dmp m = new Dmp(); if (fields.hasField(this.asIndexer(Dmp._id))) m.setId(d.getId()); @@ -91,6 +91,7 @@ public class DmpBuilder extends BaseBuilder { if (!blueprintFields.isEmpty() && blueprintItemsMap != null && blueprintItemsMap.containsKey(d.getBlueprintId())) m.setBlueprint(blueprintItemsMap.get(d.getBlueprintId())); if (dmpReferencesMap != null && !dmpReferencesMap.isEmpty() && dmpReferencesMap.containsKey(d.getId())) m.setDmpReferences(dmpReferencesMap.get(d.getId())); if (dmpUsersMap != null && !dmpUsersMap.isEmpty() && dmpUsersMap.containsKey(d.getId())) m.setDmpUsers(dmpUsersMap.get(d.getId())); + if (descriptionsMap != null && !descriptionsMap.isEmpty() && descriptionsMap.containsKey(d.getId())) m.setDescriptions(descriptionsMap.get(d.getId())); models.add(m); } @@ -196,4 +197,23 @@ public class DmpBuilder extends BaseBuilder { return itemMap; } + private Map> collectDmpDescriptions(FieldSet fields, List data) throws MyApplicationException { + if (fields.isEmpty() || data.isEmpty()) return null; + this.logger.debug("checking related - {}", Description.class.getSimpleName()); + + Map> itemMap; + FieldSet clone = new BaseFieldSet(fields.getFields()).ensure(this.asIndexer(Description._dmp, Dmp._id)); + DmpQuery dmpQuery = this.queryFactory.query(DmpQuery.class).authorize(this.authorize).ids(data.stream().map(DmpEntity::getId).distinct().collect(Collectors.toList())); + DescriptionQuery query = this.queryFactory.query(DescriptionQuery.class).authorize(this.authorize).dmpSubQuery(dmpQuery); + itemMap = this.builderFactory.builder(DescriptionBuilder.class).authorize(this.authorize).asMasterKey(query, clone, x -> x.getDmp().getId()); + + if (!fields.hasField(this.asIndexer(Description._dmp, Dmp._id))) { + itemMap.values().stream().flatMap(List::stream).filter(x -> x != null && x.getDmp() != null).peek(x -> { + x.getDmp().setId(null); + }); + } + + return itemMap; + } + } diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index 21f984bb8..33dc5520a 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -46,8 +46,8 @@ const appRoutes: Routes = [ } }, { - path: 'datasets', - loadChildren: () => import('./ui/dataset/dataset.module').then(m => m.DatasetModule), + path: 'descriptions', + loadChildren: () => import('./ui/description/description.module').then(m => m.DescriptionModule), data: { breadcrumb: true, title: 'GENERAL.TITLES.DESCRIPTIONS' diff --git a/dmp-frontend/src/app/app.component.ts b/dmp-frontend/src/app/app.component.ts index 46b681c47..457b9e729 100644 --- a/dmp-frontend/src/app/app.component.ts +++ b/dmp-frontend/src/app/app.component.ts @@ -225,10 +225,10 @@ export class AppComponent implements OnInit, AfterViewInit { // } initializeServices() { - this.translate.setDefaultLang(this.configurationService.defaultLanguage || 'en'); + this.translate.setDefaultLang(this.language.getDefaultLanguagesCode()); this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileCulture() ? this.cultureService.cultureSelected(this.authentication.getUserProfileCulture()) : this.cultureService.cultureSelected(this.configurationService.defaultCulture); this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileTimezone() ? this.timezoneService.timezoneSelected(this.authentication.getUserProfileTimezone()) : this.timezoneService.timezoneSelected(this.configurationService.defaultTimezone); - this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileLanguage() ? this.language.changeLanguage(this.authentication.getUserProfileLanguage()) : (this.configurationService.defaultLanguage || 'en'); + this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileLanguage() ? this.language.changeLanguage(this.authentication.getUserProfileLanguage()) : (this.language.getDefaultLanguagesCode()); } toggleNavbar(event) { diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index 8eb218add..c99d555bd 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -20,31 +20,31 @@ import { ReloadHelperComponent } from '@app/ui/misc/reload-helper/reload-helper. import { NavbarModule } from '@app/ui/navbar/navbar.module'; import { SidebarModule } from '@app/ui/sidebar/sidebar.module'; import { MomentUtcDateAdapter } from '@common/date/moment-utc-date-adapter'; +import { BaseHttpParams } from '@common/http/base-http-params'; import { CommonHttpModule } from '@common/http/common-http.module'; +import { InterceptorType } from '@common/http/interceptors/interceptor-type'; import { CommonUiModule } from '@common/ui/common-ui.module'; import { TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular'; import { DragulaModule } from 'ng2-dragula'; import { CookieService } from 'ngx-cookie-service'; import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsent'; import { MatomoInitializationMode, NgxMatomoModule } from 'ngx-matomo-client'; +import { from } from 'rxjs'; +import { AuthService } from './core/services/auth/auth.service'; import { ConfigurationService } from './core/services/configuration/configuration.service'; import { CultureService } from './core/services/culture/culture-service'; +import { LanguageHttpService } from './core/services/language/language.http.service'; +import { LanguageService } from './core/services/language/language.service'; import { TranslateServerLoader } from './core/services/language/server.loader'; import { MatomoService } from './core/services/matomo/matomo-service'; import { GuidedTourModule } from './library/guided-tour/guided-tour.module'; import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module'; import { OpenDMPCustomTranslationCompiler } from './utilities/translate/opendmp-custom-translation-compiler'; -import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular'; -import { BaseHttpParams } from '@common/http/base-http-params'; -import { InterceptorType } from '@common/http/interceptors/interceptor-type'; -import { from } from 'rxjs'; -import { AuthService } from './core/services/auth/auth.service'; // AoT requires an exported function for factories -export function HttpLoaderFactory(http: HttpClient, appConfig: ConfigurationService) { - return new TranslateServerLoader(http, appConfig); - //GK: In case we want the original translation provider uncomment the line below. - //return new TranslateHttpLoader(http, 'assets/i18n/', '.json'); +export function HttpLoaderFactory(languageHttpService: LanguageHttpService) { + return new TranslateServerLoader(languageHttpService); } const cookieConfig: NgcCookieConsentConfig = { @@ -82,8 +82,10 @@ const appearance: MatFormFieldDefaultOptions = { // appearance: 'standard' }; -export function InstallationConfigurationFactory(appConfig: ConfigurationService, keycloak: KeycloakService, authService: AuthService) { - return () => appConfig.loadConfiguration().then(x => keycloak.init({ +export function InstallationConfigurationFactory(appConfig: ConfigurationService, keycloak: KeycloakService, authService: AuthService, languageService: LanguageService) { + return () => appConfig.loadConfiguration().then(() => { + return languageService.loadAvailableLanguages().toPromise(); + }).then(x => keycloak.init({ config: { url: appConfig.keycloak.address, realm: appConfig.keycloak.realm, @@ -96,7 +98,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService scope: appConfig.keycloak.scope, pkceMethod: 'S256' }, - shouldAddToken: () => false + shouldAddToken: () => false }).then(() => { const params = new BaseHttpParams(); params.interceptorContext = { @@ -108,7 +110,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService ] }; const tokenPromise = keycloak.getToken(); - return authService.prepareAuthRequest(from(tokenPromise), {params}).toPromise().catch(error => authService.onAuthenticateError(error)); + return authService.prepareAuthRequest(from(tokenPromise), { params }).toPromise().catch(error => authService.onAuthenticateError(error)); })); } @@ -126,7 +128,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, - deps: [HttpClient, ConfigurationService] + deps: [LanguageHttpService] } }), HttpClientModule, @@ -161,7 +163,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService { provide: APP_INITIALIZER, useFactory: InstallationConfigurationFactory, - deps: [ConfigurationService, KeycloakService, AuthService], + deps: [ConfigurationService, KeycloakService, AuthService, LanguageService], multi: true }, { diff --git a/dmp-frontend/src/app/core/common/enum/description-status.ts b/dmp-frontend/src/app/core/common/enum/description-status.ts index 6055bf5db..ab1a4210f 100644 --- a/dmp-frontend/src/app/core/common/enum/description-status.ts +++ b/dmp-frontend/src/app/core/common/enum/description-status.ts @@ -1,4 +1,5 @@ export enum DescriptionStatus { Draft = 0, - Finalized = 1 + Finalized = 1, + Canceled = 2 } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/dmp-access-type.ts b/dmp-frontend/src/app/core/common/enum/dmp-access-type.ts new file mode 100644 index 000000000..c49005b1c --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/dmp-access-type.ts @@ -0,0 +1,4 @@ +export enum DmpAccessType { + Public = 0, + Restricted = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/dmp-user-role.ts b/dmp-frontend/src/app/core/common/enum/dmp-user-role.ts new file mode 100644 index 000000000..02a8b69c9 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/dmp-user-role.ts @@ -0,0 +1,4 @@ +export enum DmpUserRole { + Owner = 0, + User = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/dmp-version-status.ts b/dmp-frontend/src/app/core/common/enum/dmp-version-status.ts new file mode 100644 index 000000000..e03a98579 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/dmp-version-status.ts @@ -0,0 +1,4 @@ +export enum DmpVersionStatus { + Current = 0, + Previous = 1 +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 4632bf919..af89cee8a 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -17,7 +17,7 @@ import { DepositRepositoriesService } from './services/deposit-repositories/depo import { DescriptionTemplateTypeService } from './services/description-template-type/description-template-type.service'; import { DmpBlueprintService } from './services/dmp/dmp-blueprint.service'; import { DmpInvitationService } from './services/dmp/dmp-invitation.service'; -import { DmpService } from './services/dmp/dmp.service'; +import { DmpService, DmpServiceNew } from './services/dmp/dmp.service'; import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service'; import { ExternalDataRepositoryService } from './services/external-sources/data-repository/extternal-data-repository.service'; import { ExternalDatasetService } from './services/external-sources/dataset/external-dataset.service'; @@ -61,7 +61,8 @@ import { UserSettingsService } from './services/user-settings/user-settings.serv import { UserService } from './services/user/user.service'; import { FileUtils } from './services/utilities/file-utils.service'; import { QueryParamsService } from './services/utilities/query-params.service'; -import { LanguageV2Service } from './services/language/language-v2.service'; +import { LanguageHttpService } from './services/language/language.http.service'; +import { DescriptionService } from './services/description/description.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -142,7 +143,9 @@ export class CoreServiceModule { ReferenceTypeService, TenantService, UserService, - LanguageV2Service + LanguageHttpService, + DmpServiceNew, + DescriptionService ], }; } diff --git a/dmp-frontend/src/app/core/model/description/description.ts b/dmp-frontend/src/app/core/model/description/description.ts index fb64949f9..a28bc8b7c 100644 --- a/dmp-frontend/src/app/core/model/description/description.ts +++ b/dmp-frontend/src/app/core/model/description/description.ts @@ -3,35 +3,71 @@ import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model"; import { Guid } from "@common/types/guid"; import { Dmp } from "../dmp/dmp"; import { DescriptionTemplate } from "../description-template/description-template"; -import { DescriptionReference } from "./description-reference"; +import { User } from "../user/user"; +import { Reference, ReferencePersist } from "../reference/reference"; +import { Tag } from "../tag/tag"; export interface Description extends BaseEntity { label: string; + properties: PropertyDefinition; + status: DescriptionStatus; description?: String; - dmp: Dmp; - uri: String; - status: DescriptionStatus + createdBy: User; finalizedAt: Date; - dmpSectionIndex?: number; - template: DescriptionTemplate; descriptionReferences: DescriptionReference[]; + descriptionTags: DescriptionTag[]; + template: DescriptionTemplate; + dmp: Dmp; +} +export interface PublicDescription extends BaseEntity { +} +export interface PropertyDefinition { + fields?: DescriptionField[]; +} - // registries?: RegistryModel[]; - // services?: ServiceModel[]; - // dataRepositories?: DataRepositoryModel[]; - // tags?: TagModel[]; - // externalDatasets?: ExternalDatasetModel[]; - // isProfileLatestVersion?: Boolean; - // modified?: Date; +export interface DescriptionField { + key?: string; + value: string; +} + +export interface DescriptionReference extends BaseEntity { + description?: Description; + reference?: Reference; +} + +export interface DescriptionTag extends BaseEntity { + description?: Description; + tag?: Tag; } // // Persist // export interface DescriptionPersist extends BaseEntityPersist { + label: string; + dmpId: Guid; + dmpDescriptionTemplateId: Guid; + descriptionTemplateId: Guid; + status: DescriptionStatus; + description: string; + properties: PropertyDefinitionPersist; + tags: string[]; + references: DescriptionReferencePersist[]; +} +export interface PropertyDefinitionPersist{ + fields: DescriptionFieldPersist[]; +} + +export interface DescriptionFieldPersist { + key?: string; + value: string; +} + +export interface DescriptionReferencePersist extends BaseEntity { + reference?: ReferencePersist; } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts b/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts index bf74fd1e3..8c009455a 100644 --- a/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts +++ b/dmp-frontend/src/app/core/model/dmp/dmp-reference.ts @@ -9,13 +9,4 @@ export interface DmpReference extends BaseEntity { dmp?: Dmp; reference?: Reference; data: String; -} - -// -// Persist -// -export interface DmpPersist extends BaseEntityPersist { - dmpId: Guid; - referenceId: Guid; - data: String; } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/dmp/dmp.ts b/dmp-frontend/src/app/core/model/dmp/dmp.ts index abcace618..9b4ff57c4 100644 --- a/dmp-frontend/src/app/core/model/dmp/dmp.ts +++ b/dmp-frontend/src/app/core/model/dmp/dmp.ts @@ -5,16 +5,20 @@ import { GrantListingModel } from "../grant/grant-listing"; import { OrganizationModel } from "../organisation/organization"; import { ProjectModel } from "../project/project"; import { ResearcherModel } from "../researcher/researcher"; -import { UserModel } from "../user/user"; +import { User, UserModel } from "../user/user"; import { UserInfoListingModel } from "../user/user-info-listing"; import { DmpDatasetProfile } from "./dmp-dataset-profile/dmp-dataset-profile"; import { DmpDynamicField } from "./dmp-dynamic-field"; import { DmpExtraField } from "./dmp-extra-field"; import { BaseEntity, BaseEntityPersist } from '@common/base/base-entity.model'; import { Description } from '../description/description'; -import { DmpReference } from '../reference/reference'; +import { DmpReference, ReferencePersist } from '../reference/reference'; +import { DmpVersionStatus } from '@app/core/common/enum/dmp-version-status'; +import { DmpAccessType } from '@app/core/common/enum/dmp-access-type'; +import { DmpUserRole } from '@app/core/common/enum/dmp-user-role'; +import { Guid } from '@common/types/guid'; -export interface DmpModel { +export interface DmpModel { //TODO: Delete id: string; label: string; groupId: String; @@ -49,21 +53,25 @@ export interface DmpBlueprint { export interface Dmp extends BaseEntity { label: string; - description: String; version: number; status: DmpStatus; + versionStatus: DmpVersionStatus; + properties: string; groupId: String; - - + description: String; finalizedAt: Date; + publishedAt: Date; + creator: User; + accessType: DmpAccessType; blueprint: DmpBlueprint; - dmpDescriptions: Description[]; - dmpReferences: DmpReference + language: String; + publicAfter: Date; + dmpReferences: DmpReference[]; + dmpUsers: DmpUser[]; + descriptions: Description[]; - lockable: boolean; - - + // TODO: delete // grant: GrantListingModel; // project: ProjectModel; // funder: FunderModel; @@ -75,7 +83,7 @@ export interface Dmp extends BaseEntity { // profiles: DmpDatasetProfile[]; - + // associatedUsers: UserModel[]; // users: UserInfoListingModel[]; // creator: UserModel; @@ -85,13 +93,53 @@ export interface Dmp extends BaseEntity { // language: String; } +export interface DmpUser extends BaseEntity { + dmp: Dmp; + user: User; + role: DmpUserRole; +} + // // Persist // export interface DmpPersist extends BaseEntityPersist { label: string; - description: String; - version: number; status: DmpStatus; - groupId: String; + properties: string; + description: String; + language: String; + blueprint: DmpBlueprint; + accessType: DmpAccessType; + references: DmpReferencePersist[]; + descriptionTemplates: DmpDescriptionTemplatePersist[]; +} + +export interface DmpReferencePersist extends BaseEntityPersist { + reference: ReferencePersist; + data: string; +} + +export interface DmpDescriptionTemplatePersist extends BaseEntityPersist { + descriptionTemplateGroupId: Guid; + sectionId: Guid; +} + +export interface CloneDmpPersist { + id: Guid; + label: string; + description: String; + descriptions: Guid[]; +} + +export interface NewVersionDmpPersist { + id: Guid; + label: string; + description: String; + blueprintId: Guid; + descriptions: Guid[]; +} + +export interface DmpUserPersist { + user: Guid; + role: DmpUserRole; } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/model/tag/tag.ts b/dmp-frontend/src/app/core/model/tag/tag.ts index b38f2ddc8..68204c2c9 100644 --- a/dmp-frontend/src/app/core/model/tag/tag.ts +++ b/dmp-frontend/src/app/core/model/tag/tag.ts @@ -1,4 +1,12 @@ -export interface TagModel { +import { BaseEntity } from "@common/base/base-entity.model"; +import { User } from "../user/user"; + +export interface TagModel { //TODO: old entity, delete id: string; name: string; } + +export interface Tag extends BaseEntity { + label?: string; + createdBy?: User; +} diff --git a/dmp-frontend/src/app/core/query/description.lookup.ts b/dmp-frontend/src/app/core/query/description.lookup.ts new file mode 100644 index 000000000..8ce4e3fd2 --- /dev/null +++ b/dmp-frontend/src/app/core/query/description.lookup.ts @@ -0,0 +1,35 @@ +import { Lookup } from '@common/model/lookup'; +import { Guid } from '@common/types/guid'; +import { IsActive } from '../common/enum/is-active.enum'; +import { DescriptionStatus } from '../common/enum/description-status'; +import { DmpLookup } from './dmp.lookup'; + +export class DescriptionLookup extends Lookup implements DescriptionFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + createdAfter: Date; + createdBefore: Date; + finalizedAfter: Date; + finalizedBefore: Date; + dmpSubQuery: DmpLookup; + isActive: IsActive[]; + statuses: DescriptionStatus[]; + + constructor() { + super(); + } +} + +export interface DescriptionFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + createdAfter: Date; + createdBefore: Date; + finalizedAfter: Date; + finalizedBefore: Date; + dmpSubQuery: DmpLookup; + isActive: IsActive[]; + statuses: DescriptionStatus[]; +} diff --git a/dmp-frontend/src/app/core/query/dmp.lookup.ts b/dmp-frontend/src/app/core/query/dmp.lookup.ts new file mode 100644 index 000000000..72d341a5e --- /dev/null +++ b/dmp-frontend/src/app/core/query/dmp.lookup.ts @@ -0,0 +1,32 @@ +import { Lookup } from '@common/model/lookup'; +import { Guid } from '@common/types/guid'; +import { IsActive } from '../common/enum/is-active.enum'; +import { DmpStatus } from '../common/enum/dmp-status'; +import { DmpVersionStatus } from '../common/enum/dmp-version-status'; +import { DmpAccessType } from '../common/enum/dmp-access-type'; + +export class DmpLookup extends Lookup implements DmpFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + versionStatuses: DmpVersionStatus[]; + statuses: DmpStatus[]; + accessTypes: DmpAccessType[]; + versions: Number[] + + constructor() { + super(); + } +} + +export interface DmpFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + versionStatuses: DmpVersionStatus[]; + statuses: DmpStatus[]; + accessTypes: DmpAccessType[]; + versions: Number[] +} diff --git a/dmp-frontend/src/app/core/services/configuration/configuration.service.ts b/dmp-frontend/src/app/core/services/configuration/configuration.service.ts index 89be3d64a..a83be2201 100644 --- a/dmp-frontend/src/app/core/services/configuration/configuration.service.ts +++ b/dmp-frontend/src/app/core/services/configuration/configuration.service.ts @@ -47,16 +47,6 @@ export class ConfigurationService extends BaseComponent { return this._defaultTimezone || 'UTC'; } - private _defaultLanguage: string; - get defaultLanguage(): string { - return this._defaultLanguage; - } - - private _availableLanguages: any[] = []; - get availableLanguages(): any[] { - return this._availableLanguages; - } - private _logging: Logging; get logging(): Logging { return this._logging; @@ -161,8 +151,6 @@ export class ConfigurationService extends BaseComponent { this._defaultCulture = config.defaultCulture; this._defaultBlueprintId = config.defaultBlueprintId; this._defaultTimezone = config.defaultTimezone; - this._defaultLanguage = config.defaultLanguage; - this._availableLanguages = config.availableLanguages; this._keycloak = KeycloakConfiguration.parseValue(config.keycloak); this._logging = Logging.parseValue(config.logging); this._lockInterval = config.lockInterval; diff --git a/dmp-frontend/src/app/core/services/description/description.service.ts b/dmp-frontend/src/app/core/services/description/description.service.ts new file mode 100644 index 000000000..0a1b90308 --- /dev/null +++ b/dmp-frontend/src/app/core/services/description/description.service.ts @@ -0,0 +1,110 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { Description, DescriptionPersist, PublicDescription } from '@app/core/model/description/description'; +import { DescriptionLookup } from '@app/core/query/description.lookup'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { QueryResult } from '@common/model/query-result'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; +import { ConfigurationService } from '../configuration/configuration.service'; +import { BaseHttpV2Service } from '../http/base-http-v2.service'; + +@Injectable() +export class DescriptionService { + + private headers = new HttpHeaders(); + + constructor(private http: BaseHttpV2Service, private httpClient: HttpClient, private configurationService: ConfigurationService, private filterService: FilterService) { + } + + private get apiBase(): string { return `${this.configurationService.server}description`; } + + query(q: DescriptionLookup): Observable> { + const url = `${this.apiBase}/query`; + return this.http.post>(url, q).pipe(catchError((error: any) => throwError(error))); + } + + publicQuery(q: DescriptionLookup): Observable> { + const url = `${this.apiBase}/public/query`; + return this.http.post>(url, q).pipe(catchError((error: any) => throwError(error))); + } + + getSingle(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/${id}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + getPublicSingle(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/public/${id}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + persist(item: DescriptionPersist): Observable { + const url = `${this.apiBase}/persist`; + + return this.http + .post(url, item).pipe( + catchError((error: any) => throwError(error))); + } + + delete(id: Guid): Observable { + const url = `${this.apiBase}/${id}`; + + return this.http + .delete(url).pipe( + catchError((error: any) => throwError(error))); + } + + // + // Autocomplete Commons + // + // tslint:disable-next-line: member-ordering + singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.query(this.buildAutocompleteLookup()).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteLookup(searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: Description) => item.label, + titleFn: (item: Description) => item.label, + valueAssign: (item: Description) => item.id, + }; + + // tslint:disable-next-line: member-ordering + multipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = { + initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)), + getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)), + displayFn: (item: Description) => item.label, + titleFn: (item: Description) => item.label, + valueAssign: (item: Description) => item.id, + }; + + private buildAutocompleteLookup(like?: string, excludedIds?: Guid[], ids?: Guid[]): DescriptionLookup { + const lookup: DescriptionLookup = new DescriptionLookup(); + lookup.page = { size: 100, offset: 0 }; + if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } + if (ids && ids.length > 0) { lookup.ids = ids; } + lookup.isActive = [IsActive.Active]; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label) + ] + }; + lookup.order = { items: [nameof(x => x.label)] }; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } +} diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 0c75c0738..4d215065b 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -1,7 +1,17 @@ import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { environment } from '../../../../environments/environment'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; +import { VersionListingModel } from '@app/core/model/version/version-listing.model'; +import { DmpLookup } from '@app/core/query/dmp.lookup'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { QueryResult } from '@common/model/query-result'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; import { BaseHttpParams } from '../../../../common/http/base-http-params'; import { InterceptorType } from '../../../../common/http/interceptors/interceptor-type'; import { DynamicFieldGrantCriteria } from '../../../models/dynamic-field-grant/DynamicFieldGrantCriteria'; @@ -10,17 +20,152 @@ import { DataTableRequest } from '../../model/data-table/data-table-request'; import { DatasetListingModel } from '../../model/dataset/dataset-listing'; import { DatasetProfileModel } from '../../model/dataset/dataset-profile'; import { DatasetsToBeFinalized } from '../../model/dataset/datasets-toBeFinalized'; -import { DmpModel } from '../../model/dmp/dmp'; +import { CloneDmpPersist, Dmp, DmpModel, DmpPersist, DmpUser, DmpUserPersist, NewVersionDmpPersist } from '../../model/dmp/dmp'; import { DmpListingModel } from '../../model/dmp/dmp-listing'; import { DmpOverviewModel } from '../../model/dmp/dmp-overview'; import { DatasetProfileCriteria } from '../../query/dataset-profile/dataset-profile-criteria'; import { DmpCriteria } from '../../query/dmp/dmp-criteria'; import { ExploreDmpCriteriaModel } from '../../query/explore-dmp/explore-dmp-criteria'; import { RequestItem } from '../../query/request-item'; -import { BaseHttpService } from '../http/base-http.service'; import { ConfigurationService } from '../configuration/configuration.service'; -import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; -import { VersionListingModel } from '@app/core/model/version/version-listing.model'; +import { BaseHttpV2Service } from '../http/base-http-v2.service'; +import { BaseHttpService } from '../http/base-http.service'; + +@Injectable() +export class DmpServiceNew { + + private headers = new HttpHeaders(); + + constructor(private http: BaseHttpV2Service, private httpClient: HttpClient, private configurationService: ConfigurationService, private filterService: FilterService) { + } + + private get apiBase(): string { return `${this.configurationService.server}dmp`; } + + query(q: DmpLookup): Observable> { + const url = `${this.apiBase}/query`; + return this.http.post>(url, q).pipe(catchError((error: any) => throwError(error))); + } + + getSingle(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/${id}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + persist(item: DmpPersist): Observable { + const url = `${this.apiBase}/persist`; + + return this.http + .post(url, item).pipe( + catchError((error: any) => throwError(error))); + } + + delete(id: Guid): Observable { + const url = `${this.apiBase}/${id}`; + + return this.http + .delete(url).pipe( + catchError((error: any) => throwError(error))); + } + + clone(item: CloneDmpPersist, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/clone`; + const options = { params: { f: reqFields } }; + + return this.http + .post(url, item).pipe( + catchError((error: any) => throwError(error))); + } + + newVersion(item: NewVersionDmpPersist, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/new-version`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + assignUsers(id: Guid, items: DmpUserPersist[], reqFields: string[] = []): Observable { + const url = `${this.apiBase}/${id}/assign-users`; + const options = { params: { f: reqFields } }; + + return this.http + .post(url, items).pipe( + catchError((error: any) => throwError(error))); + } + + downloadXML(id: Guid): Observable> { + const url = `${this.apiBase}/xml/export/${id}`; + let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml'); + const params = new BaseHttpParams(); + params.interceptorContext = { + excludedInterceptors: [InterceptorType.JSONContentType] + }; + return this.httpClient.get(url, { params: params, responseType: 'blob', observe: 'response', headers: headerXml }); + } + + uploadFile(file: FileList, labelSent: string, reqFields: string[] = []): Observable> { + const url = `${this.apiBase}/xml/import`; + const params = new BaseHttpParams(); + params.interceptorContext = { + excludedInterceptors: [InterceptorType.JSONContentType] + }; + const formData = new FormData(); + formData.append('file', file[0], labelSent); + return this.http.post(url, formData, { params: params }); + } + + // + // Autocomplete Commons + // + // tslint:disable-next-line: member-ordering + singleAutocompleteConfiguration: SingleAutoCompleteConfiguration = { + initialItems: (data?: any) => this.query(this.buildAutocompleteLookup()).pipe(map(x => x.items)), + filterFn: (searchQuery: string, data?: any) => this.query(this.buildAutocompleteLookup(searchQuery)).pipe(map(x => x.items)), + getSelectedItem: (selectedItem: any) => this.query(this.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])), + displayFn: (item: Dmp) => item.label, + titleFn: (item: Dmp) => item.label, + valueAssign: (item: Dmp) => item.id, + }; + + // tslint:disable-next-line: member-ordering + multipleAutocompleteConfiguration: MultipleAutoCompleteConfiguration = { + initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)), + filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)), + getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)), + displayFn: (item: Dmp) => item.label, + titleFn: (item: Dmp) => item.label, + valueAssign: (item: Dmp) => item.id, + }; + + private buildAutocompleteLookup(like?: string, excludedIds?: Guid[], ids?: Guid[]): DmpLookup { + const lookup: DmpLookup = new DmpLookup(); + lookup.page = { size: 100, offset: 0 }; + if (excludedIds && excludedIds.length > 0) { lookup.excludedIds = excludedIds; } + if (ids && ids.length > 0) { lookup.ids = ids; } + lookup.isActive = [IsActive.Active]; + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label) + ] + }; + lookup.order = { items: [nameof(x => x.label)] }; + if (like) { lookup.like = this.filterService.transformLike(like); } + return lookup; + } +} + + +// +// +// Pre refactor TODO: delete +// +// @Injectable() export class DmpService { diff --git a/dmp-frontend/src/app/core/services/language/language-v2.service.ts b/dmp-frontend/src/app/core/services/language/language.http.service.ts similarity index 99% rename from dmp-frontend/src/app/core/services/language/language-v2.service.ts rename to dmp-frontend/src/app/core/services/language/language.http.service.ts index 0bbc1a824..aff0f3a79 100644 --- a/dmp-frontend/src/app/core/services/language/language-v2.service.ts +++ b/dmp-frontend/src/app/core/services/language/language.http.service.ts @@ -14,7 +14,7 @@ import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpV2Service } from '../http/base-http-v2.service'; @Injectable() -export class LanguageV2Service { +export class LanguageHttpService { constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService, private filterService: FilterService) { } diff --git a/dmp-frontend/src/app/core/services/language/language.service.ts b/dmp-frontend/src/app/core/services/language/language.service.ts index f4c671b3f..bfc44ea0a 100644 --- a/dmp-frontend/src/app/core/services/language/language.service.ts +++ b/dmp-frontend/src/app/core/services/language/language.service.ts @@ -1,26 +1,20 @@ import { Injectable } from '@angular/core'; +import { BaseService } from '@common/base/base.service'; import { TranslateService } from '@ngx-translate/core'; -import { environment } from 'environments/environment'; import { Observable } from 'rxjs'; -import { HttpResponse, HttpClient } from '@angular/common/http'; -import { BaseHttpService } from '../http/base-http.service'; -import { Language } from '@app/models/language/Language'; -import { ConfigurationService } from '../configuration/configuration.service'; -import { BaseHttpParams } from '@common/http/base-http-params'; -import { InterceptorType } from '@common/http/interceptors/interceptor-type'; +import { map, takeUntil } from 'rxjs/operators'; +import { LanguageHttpService } from './language.http.service'; @Injectable() -export class LanguageService { +export class LanguageService extends BaseService { private currentLanguage: string; - private get apiBase(): string { return `${this.configurationService.server}language`; } + private availableLanguageCodes: string[]; constructor( private translate: TranslateService, - private http: HttpClient, - private baseHttp: BaseHttpService, - private configurationService: ConfigurationService + private languageHttpService: LanguageHttpService ) { - this.currentLanguage = this.configurationService.defaultLanguage || 'en'; + super(); } public changeLanguage(lang: string) { @@ -29,31 +23,28 @@ export class LanguageService { } public getCurrentLanguage() { + if (this.currentLanguage == null) throw new Error("languages not loaded"); return this.currentLanguage; } - public getCurrentLanguageJSON(): Observable> { - const params = new BaseHttpParams(); - // params.interceptorContext = { - // excludedInterceptors: [ - // InterceptorType.AuthToken, - // ] - // }; - return this.http.get(`${this.apiBase}/${this.currentLanguage}`, { params: params, responseType: 'blob', observe: 'response' }); + public loadAvailableLanguages(): Observable { + return this.languageHttpService.queryAvailableCodes(this.languageHttpService.buildAutocompleteLookup()) + .pipe(takeUntil(this._destroyed)).pipe( + map((data) => { + this.availableLanguageCodes = data.items; + if (this.availableLanguageCodes.length > 0) this.currentLanguage = this.availableLanguageCodes[0]; + return this.availableLanguageCodes; + }) + ); } - public updateLanguage(json: string): Observable { - return this.baseHttp.post(`${this.apiBase}/update/${this.currentLanguage}`, json); + public getDefaultLanguagesCode() { + if (this.availableLanguageCodes == null || this.availableLanguageCodes.length == 0) throw new Error("languages not loaded"); + return this.availableLanguageCodes[0]; } - public getCurrentLanguageName() { - let result: string = ''; - this.configurationService.availableLanguages.forEach(language => { - if (language.value === this.currentLanguage) { - result = this.translate.instant(language.label); - } - }); - return result; + public getAvailableLanguagesCodes() { + if (this.availableLanguageCodes == null) throw new Error("languages not loaded"); + return this.availableLanguageCodes; } - } diff --git a/dmp-frontend/src/app/core/services/language/server.loader.ts b/dmp-frontend/src/app/core/services/language/server.loader.ts index 1a2dfbabc..f794533a0 100644 --- a/dmp-frontend/src/app/core/services/language/server.loader.ts +++ b/dmp-frontend/src/app/core/services/language/server.loader.ts @@ -5,23 +5,23 @@ import { HttpClient } from '@angular/common/http'; import { ConfigurationService } from '../configuration/configuration.service'; import { BaseHttpParams } from '@common/http/base-http-params'; import { InterceptorType } from '@common/http/interceptors/interceptor-type'; +import { LanguageHttpService } from './language.http.service'; +import { map } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; +import { Language } from '@app/core/model/language/language'; -export class TranslateServerLoader implements TranslateLoader{ - private get apiBase(): string { return `${this.configurationService.server}language`; } +export class TranslateServerLoader implements TranslateLoader { constructor( - private http: HttpClient, - private configurationService: ConfigurationService + private languageHttpService: LanguageHttpService ) { } getTranslation(lang: string): Observable { - const params = new BaseHttpParams(); - // params.interceptorContext = { - // excludedInterceptors: [ - // InterceptorType.AuthToken, - // ] - // }; - return this.http.get(`${this.apiBase}/${lang}`, { params: params }); + return this.languageHttpService.getSingleWithCode(lang, [ + ...nameof(x => x.id), + nameof(x => x.code), + nameof(x => x.payload), + ]).pipe(map(x => JSON.parse(x.payload))); } } diff --git a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts index d7f66a1ff..b93725ec7 100644 --- a/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/language/editor/language-editor.component.ts @@ -27,7 +27,7 @@ import { map, takeUntil } from 'rxjs/operators'; import { LanguageEditorResolver } from './language-editor.resolver'; import { LanguageEditorService } from './language-editor.service'; import { LanguageEditorModel } from './language-editor.model'; -import { LanguageV2Service } from '@app/core/services/language/language-v2.service'; +import { LanguageHttpService } from '@app/core/services/language/language.http.service'; @Component({ @@ -76,7 +76,7 @@ export class LanguageEditorComponent extends BaseEditor +
+
{{'DATASET-WIZARD.DIALOGUE.TITLE' | translate}}
+
close
+
+
+ + + + + +
+
+
+
+
+ diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.scss new file mode 100644 index 000000000..69f3e2062 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.scss @@ -0,0 +1,50 @@ +.confirmation-message { + padding-bottom: 20px; +} + +.close-btn { + cursor: pointer; +} + +.dataset-copy-dialog { + margin-bottom: 1.125rem; +} + +.cancel-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border: 1px solid #b5b5b5; + border-radius: 30px; +min-width: 101px; + height: 43px; + color: #212121; + font-weight: 500; +} + +.confirm-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border: 1px solid var(--primary-color); + border-radius: 30px; + opacity: 1; +min-width: 101px; + height: 43px; + color: var(--primary-color); + font-weight: 500; +} + +.confirm-btn:hover { + background-color: var(--primary-color); + color: #ffffff; +} + +.close-icon { + cursor: pointer; + // margin-right: 20px; + padding: .4rem; + width: auto !important; + height: auto !important; +} + +.close-icon:hover { + background-color: #ECECED !important; + border-radius: 50%; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.ts new file mode 100644 index 000000000..dc98a3e37 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component.ts @@ -0,0 +1,95 @@ + +import { map, filter } from 'rxjs/operators'; +import { Component } from "@angular/core"; +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { SingleAutoCompleteConfiguration } from "../../../../library/auto-complete/single/single-auto-complete-configuration"; +import { Observable } from "rxjs"; +import { DataTableRequest } from "../../../../core/model/data-table/data-table-request"; +import { DmpCriteria } from "../../../../core/query/dmp/dmp-criteria"; +import { DmpListingModel } from "../../../../core/model/dmp/dmp-listing"; +import { DmpService } from "../../../../core/services/dmp/dmp.service"; +import { Inject } from "@angular/core"; +import { DmpModel } from "../../../../core/model/dmp/dmp"; +import { TranslateService } from "@ngx-translate/core"; + +@Component({ + selector: 'dataset-copy-dialogue-component', + templateUrl: 'dataset-copy-dialogue.component.html', + styleUrls: ['./dataset-copy-dialogue.component.scss'], +}) +export class DatasetCopyDialogueComponent { + + dmpAutoCompleteConfiguration: SingleAutoCompleteConfiguration; + dmpModel: DmpModel; + datasetDescriptionTemplateLabel: String; + + constructor( + public dialogRef: MatDialogRef, + public dmpService: DmpService, + public language: TranslateService, + @Inject(MAT_DIALOG_DATA) public data: any + ) { } + + ngOnInit() { + this.dmpAutoCompleteConfiguration = { + filterFn: this.searchDmp.bind(this), + initialItems: (extraData) => this.searchDmp(''), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'], + }; + } + + cancel() { + this.dialogRef.close(this.data); + } + + confirm() { + this.datasetProfileValidate().subscribe(x => { + if (this.data.datasetProfileExist) { + this.dialogRef.close(this.data); + } + else if (!this.data.datasetProfileExist) { + this.data.formControl.setErrors({ 'incorrect': true }); + } + }); + } + + searchDmp(query: string): Observable { + const fields: Array = new Array(); + fields.push('asc'); + const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + dmpDataTableRequest.criteria = new DmpCriteria(); + dmpDataTableRequest.criteria.like = query; + dmpDataTableRequest.criteria.datasetTemplates = [this.data.datasetProfileId]; + return this.dmpService.getPaged(dmpDataTableRequest, "profiles").pipe(map(x => x.data)); + } + + datasetProfileValidate() { + return this.dmpService.getSingle(this.data.formControl.value.id).pipe(map(result => result as DmpModel), + map(result => { + this.dmpModel = result + this.dmpModel.profiles.forEach((element) => { + if (element.id == this.data.datasetProfileId) { + this.data.datasetProfileExist = true; + } + }) + })); + } + + getErrorMessage() { + return this.language.instant('DATASET-WIZARD.DIALOGUE.ERROR-MESSAGE'); + } + + hasValidDatasetProfile() { + if (this.data.datasetProfileExist) { + return true; + } + else { + return false; + } + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.module.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.module.ts new file mode 100644 index 000000000..d1c8a4602 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonUiModule } from '@common/ui/common-ui.module'; + +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { DatasetCopyDialogueComponent } from './dataset-copy-dialogue.component'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + AutoCompleteModule + ], + declarations: [ + DatasetCopyDialogueComponent + ] +}) +export class DatasetCopyDialogModule { } diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.html new file mode 100644 index 000000000..c56c214e4 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.html @@ -0,0 +1,107 @@ +
+
+

{{'DATASET-EDITOR.TITLE.INTRO' | translate}}

+ {{'DATASET-EDITOR.TITLE.INTRO-TIP' | translate}} +
+
+ +
+
+
1.1 {{'DATASET-EDITOR.FIELDS.TITLE' | translate}}*
+ + +
+ + + {{formGroup.get('label').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+ +
+
+
1.2 {{'DATASET-EDITOR.FIELDS.DESCRIPTION' | translate}}
+ {{'DATASET-EDITOR.HINT.DESCRIPTION' | translate}} + +
+ + +
+ {{formGroup.get('description').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} +
+
+
+
+ + + + + +
1.4 {{'DATASET-EDITOR.FIELDS.PROFILE' | translate}}*
+
+ + + +
+ {{profile.label}} +
+
+
+ {{formGroup.get('profile').getError('backendError').message}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} +
+
+
+
+ + + + diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.scss new file mode 100644 index 000000000..b006bc845 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.scss @@ -0,0 +1,117 @@ +.dataset-editor { + .intro { + text-align: left; + font-weight: 400; + letter-spacing: 0px; + color: #212121; + opacity: 1; + margin: 3rem 0rem 3rem 0rem; + } + + .heading { + text-align: left; + font-weight: 700; + font-size: 18px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-top: 1.625rem; + margin-bottom: 0.625rem; + } + + .hint { + text-align: left; + font-weight: 400; + font-size: 16px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-bottom: 2.125rem; + } + + .title-form, + .description-form, + .profile-form { + text-align: left; + font-weight: 400; + font-size: 16px; + letter-spacing: 0.15px; + color: #7d7d7d; + opacity: 1; + margin-bottom: 1rem; + } + + // textarea::placeholder { + // font-style: oblique; + // } + + .input-btn { + border: none; + color: #aaaaaa; + background-color: #ffffff00; + cursor: pointer; + } + + .input-btn :hover { + color: var(--primary-color-3) !important; + } + + .dmp-link { + color: #3fafac; + font-weight: 400; + font-size: 16px; + cursor: pointer; + } + + .dmp-link:hover { + text-decoration: underline; + } +} + +::ng-deep .title-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +::ng-deep .description-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +::ng-deep .uri-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +::ng-deep .profile-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + + + + +::ng-deep .title-form .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} + +::ng-deep .description-form .mat-form-field-appearance-outline .mat-form-field-infix { + // font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} + +::ng-deep .uri-form .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} + +::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} + +::ng-deep .profile-form .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.ts new file mode 100644 index 000000000..e9b339ad4 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-editor/dataset-editor.component.ts @@ -0,0 +1,145 @@ +import { Component, Input } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { Router } from '@angular/router'; +import { BaseComponent } from '@common/base/base.component'; +import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service'; +import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants'; +import { TranslateService } from '@ngx-translate/core'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { takeUntil } from 'rxjs/operators'; +import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; +import { MatDialog } from '@angular/material/dialog'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { Guid } from '@common/types/guid'; +import { nameof } from 'ts-simple-nameof'; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; + +@Component({ + selector: 'app-dataset-editor-component', + templateUrl: 'dataset-editor.component.html', + styleUrls: ['./dataset-editor.component.scss'] +}) +export class DatasetEditorComponent extends BaseComponent { + + @Input() formGroup: UntypedFormGroup; + // @Input() formGroup: FormGroup = null; + @Input() availableProfiles: DatasetProfileModel[]; + @Input() dmpId: string; + showUri: boolean = false; + dmpText: string = null; + viewOnly = false; + + constructor( + private router: Router, + private dmpBlueprintService: DmpBlueprintService, + private dialog: MatDialog, + private guidedTourService: GuidedTourService, + private language: TranslateService + ) { super(); } + + public dashboardTourDmp: GuidedTour = { + tourId: 'only-dmp-tour', + useOrb: true, + steps: [ + { + title: this.dmpText, + content: 'Step 1', + orientation: Orientation.Bottom, + highlightPadding: 3, + isStepUnique: true, + customTopOffset: 8 + } + ] + }; + + checkMinMax(event, profile: DatasetProfileModel) { + event.stopPropagation(); + const dmpSectionIndex = this.formGroup.get('dmpSectionIndex').value; + const blueprintId = this.formGroup.get('dmp').value.profile.id; + this.dmpBlueprintService.getSingle(blueprintId, + [ + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.description)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.hasTemplates)].join('.'), + + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.category)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.systemFieldType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.placeholder)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.description)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.required)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.ordinal)].join('.'), + + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.descriptionTemplateId)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.minMultiplicity)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), + ] + ) + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + const section = result.definition.sections[dmpSectionIndex]; + if(section.hasTemplates){ + const foundTemplate = section.descriptionTemplates.find(template => template.descriptionTemplateId === Guid.parse(profile.id)); + if (foundTemplate !== undefined) { + let count = 0; + if(this.formGroup.get('dmp').value.datasets != null){ + for(let dataset of this.formGroup.get('dmp').value.datasets){ + if(dataset.dmpSectionIndex === dmpSectionIndex && dataset.profile.id === foundTemplate.descriptionTemplateId){ + count++; + } + } + if(count === foundTemplate.maxMultiplicity){ + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DATASET-EDITOR.MAX-DESCRIPTION-DIALOG.TITLE'), + message: this.language.instant('DATASET-EDITOR.MAX-DESCRIPTION-DIALOG.MESSAGE') + }, maxWidth: '30em' + }); + } + else{ + this.formGroup.get('profile').setValue(profile); + } + } + } + else { + this.formGroup.get('profile').setValue(profile); + } + } + else { + this.formGroup.get('profile').setValue(profile); + } + }); + } + + getDmpText(): string { + return this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' + + this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' + + this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' + + this.language.instant('DMP-LISTING.GET-IDEA'); + } + + setDashboardTourDmp(label: string): void { + this.dashboardTourDmp.steps[0].title = this.getDmpText(); + this.dashboardTourDmp.steps[0].selector = '.dmp-tour-' + label; + } + + public restartTour(label: string): void { + this.setDashboardTourDmp(label); + this.guidedTourService.startTour(this.dashboardTourDmp); + + } + + public cancel(): void { + this.router.navigate(['/datasets']); + } + + public compareWith(object1: any, object2: any) { + return object1 && object2 && object1.id === object2.id; + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard-editor.model.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard-editor.model.ts new file mode 100644 index 000000000..47ec72a17 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard-editor.model.ts @@ -0,0 +1,382 @@ +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { ExternalDatasetType } from '@app/core/common/enum/external-dataset-type'; +import { DataRepositoryModel } from '@app/core/model/data-repository/data-repository'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; +import { DmpModel } from '@app/core/model/dmp/dmp'; +import { ExternalDatasetModel } from '@app/core/model/external-dataset/external-dataset'; +import { RegistryModel } from '@app/core/model/registry/registry'; +import { ServiceModel } from '@app/core/model/service/service'; +import { TagModel } from '@app/core/model/tag/tag'; +import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; +import { BackendErrorValidator } from '@common/forms/validation/custom-validator'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { ValidationContext } from '@common/forms/validation/validation-context'; + +export class DatasetWizardEditorModel { + public id: string; + public label: string; + public profile: DatasetProfileModel; + public uri: String; + public status: number; + public description: String; + public services: ExternalServiceEditorModel[] = []; + public registries: ExternalRegistryEditorModel[] = []; + public dataRepositories: ExternalDataRepositoryEditorModel[] = []; + public tags: ExternalTagEditorModel[] = []; + public externalDatasets: ExternalDatasetEditorModel[] = []; + public dmp: DmpModel; + public dmpSectionIndex: number; + public datasetProfileDefinition: DatasetDescriptionFormEditorModel; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + public isProfileLatestVersion: Boolean; + public modified: Date; + + fromModel(item: DatasetWizardModel): DatasetWizardEditorModel { + this.id = item.id; + this.label = item.label; + this.profile = item.profile; + this.uri = item.uri; + this.status = item.status; + this.description = item.description; + if (item.services) { this.services = item.services.map(x => new ExternalServiceEditorModel().fromModel(x)); } + if (item.registries) { this.registries = item.registries.map(x => new ExternalRegistryEditorModel().fromModel(x)); } + if (item.dataRepositories) { this.dataRepositories = item.dataRepositories.map(x => new ExternalDataRepositoryEditorModel().fromModel(x)); } + if (item.externalDatasets) { this.externalDatasets = item.externalDatasets.map(x => new ExternalDatasetEditorModel().fromModel(x)); } + this.dmp = item.dmp; + this.dmpSectionIndex = item.dmpSectionIndex; + if (item.datasetProfileDefinition) { this.datasetProfileDefinition = new DatasetDescriptionFormEditorModel().fromModel(item.datasetProfileDefinition); } + if (item.tags) { this.tags = item.tags.map(x => new ExternalTagEditorModel().fromModel(x)); } + this.isProfileLatestVersion = item.isProfileLatestVersion; + this.modified = new Date(item.modified); + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + if (context == null) { context = this.createValidationContext(); } + const formBuilder = new UntypedFormBuilder(); + const formGroup = formBuilder.group({ + id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + uri: [{ value: this.uri, disabled: disabled }, context.getValidation('uri').validators], + status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators], + description: [{ value: this.description, disabled: disabled }, context.getValidation('description').validators], + dmp: [{ value: this.dmp, disabled: disabled }, context.getValidation('dmp').validators], + dmpSectionIndex: [{ value: this.dmpSectionIndex, disabled: disabled }, context.getValidation('dmpSectionIndex').validators], + //externalDatasets: [{ value: this.externalDatasets, disabled: disabled }, context.getValidation('externalDatasets').validators], + tags: [{ value: this.tags, disabled: disabled }, context.getValidation('tags').validators], + //registries: [{ value: this.registries, disabled: disabled }, context.getValidation('registries').validators], + //dataRepositories: [{ value: this.dataRepositories, disabled: disabled }, context.getValidation('dataRepositories').validators], + //services: [{ value: this.services, disabled: disabled }, context.getValidation('services').validators], + profile: [{ value: this.profile, disabled: disabled }, context.getValidation('profile').validators], + modified: [{value: this.modified, disabled: disabled}, context.getValidation('modified').validators] + }); + + const externalDatasetsFormArray = new Array(); + //if (this.externalDatasets && this.externalDatasets.length > 0) { + this.externalDatasets.forEach(item => { + externalDatasetsFormArray.push(item.buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + }); + // } else { + // //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + // } + formGroup.addControl('externalDatasets', formBuilder.array(externalDatasetsFormArray)); + + // const tagsFormArray = new Array(); + // if (this.tags && this.tags.length > 0) { + // this.tags.forEach(item => { + // tagsFormArray.push(item.buildForm(context.getValidation('tags').descendantValidations, disabled)); + // }); + // } else { + // //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + // } + // formGroup.addControl('tags', formBuilder.array(tagsFormArray)); + + const registriesFormArray = new Array(); + //if (this.registries && this.registries.length > 0) { + this.registries.forEach(item => { + registriesFormArray.push(item.buildForm(context.getValidation('registries').descendantValidations, disabled)); + }); + // } else { + // //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + // } + formGroup.addControl('registries', formBuilder.array(registriesFormArray)); + + const dataRepositoriesFormArray = new Array(); + //if (this.dataRepositories && this.dataRepositories.length > 0) { + this.dataRepositories.forEach(item => { + dataRepositoriesFormArray.push(item.buildForm(context.getValidation('dataRepositories').descendantValidations, disabled)); + }); + // } else { + // //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + // } + formGroup.addControl('dataRepositories', formBuilder.array(dataRepositoriesFormArray)); + + const servicesFormArray = new Array(); + // if (this.services && this.services.length > 0) { + this.services.forEach(item => { + servicesFormArray.push(item.buildForm(context.getValidation('services').descendantValidations, disabled)); + }); + // } else { + // //externalDatasetsFormArray.push(new ExternalDatasetModel().buildForm(context.getValidation('externalDatasets').descendantValidations, disabled)); + // } + formGroup.addControl('services', formBuilder.array(servicesFormArray)); + + // const tagsFormArray = new Array(); + // this.tags.forEach(item => { + // tagsFormArray.push(item.buildForm(context.getValidation('tags').descendantValidations, disabled)); + // }); + // formGroup.addControl('tags', formBuilder.array(tagsFormArray)); + + if (this.datasetProfileDefinition) { formGroup.addControl('datasetProfileDefinition', this.datasetProfileDefinition.buildForm()); } + // formGroup.addControl('profile', this.profile.buildForm()); + return formGroup; + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + baseContext.validation.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseContext.validation.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] }); + baseContext.validation.push({ key: 'profile', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'profile')] }); + baseContext.validation.push({ key: 'uri', validators: [BackendErrorValidator(this.validationErrorModel, 'uri')] }); + baseContext.validation.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] }); + baseContext.validation.push({ key: 'description', validators: [BackendErrorValidator(this.validationErrorModel, 'description')] }); + baseContext.validation.push({ key: 'services', validators: [BackendErrorValidator(this.validationErrorModel, 'services')] }); + baseContext.validation.push({ key: 'registries', validators: [BackendErrorValidator(this.validationErrorModel, 'registries')] }); + baseContext.validation.push({ key: 'dataRepositories', validators: [BackendErrorValidator(this.validationErrorModel, 'dataRepositories')] }); + baseContext.validation.push({ key: 'externalDatasets', validators: [BackendErrorValidator(this.validationErrorModel, 'externalDatasets')] }); + baseContext.validation.push({ key: 'dmp', validators: [BackendErrorValidator(this.validationErrorModel, 'dmp')] }); + baseContext.validation.push({ key: 'dmpSectionIndex', validators: [BackendErrorValidator(this.validationErrorModel, 'dmpSectionIndex')] }); + baseContext.validation.push({ key: 'datasetProfileDefinition', validators: [BackendErrorValidator(this.validationErrorModel, 'datasetProfileDefinition')] }); + baseContext.validation.push({ key: 'tags', validators: [BackendErrorValidator(this.validationErrorModel, 'datasetProfileDefinition')] }); + baseContext.validation.push({ key: 'modified', validators: []}); + return baseContext; + } +} + +export class ExternalTagEditorModel { + public abbreviation: String; + public definition: String; + public id: String; + public name: String; + public reference: String; + public uri: String; + + constructor(id?: String, name?: String) { + this.id = id; + this.name = name; + } + + fromModel(item: TagModel): ExternalTagEditorModel { + this.id = item.id; + this.name = item.name; + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + return new UntypedFormBuilder().group({ + id: [this.id], + name: [this.name] + }); + } +} + +export class ExternalServiceEditorModel { + public id: String; + public abbreviation: String; + public definition: String; + public uri: String; + public label: String; + public reference: String; + public source: String; + + constructor(abbreviation?: String, definition?: String, id?: String, label?: String, reference?: String, uri?: String, source?: String) { + this.id = id; + this.abbreviation = abbreviation; + this.definition = definition; + this.uri = uri; + this.label = label; + this.reference = reference; + this.source = source; + } + + fromModel(item: ServiceModel): ExternalServiceEditorModel { + this.id = item.id; + this.abbreviation = item.abbreviation; + this.definition = item.definition; + this.uri = item.uri; + this.label = item.label; + this.reference = item.reference; + this.source = item.source; + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + return new UntypedFormBuilder().group({ + id: [this.id], + abbreviation: [this.abbreviation], + label: [this.label, Validators.required], + reference: [this.reference], + uri: [this.uri, Validators.required], + definition: [this.definition], + source: [this.source] + }); + } +} + +export class ExternalRegistryEditorModel { + public abbreviation: String; + public definition: String; + public id: String; + public label: String; + public reference: String; + public uri: String; + public source: String + + constructor(abbreviation?: String, definition?: String, id?: String, label?: String, reference?: String, uri?: String, source?: String) { + this.abbreviation = abbreviation; + this.definition = definition; + this.id = id; + this.label = label; + this.reference = reference; + this.uri = uri; + this.source = source; + } + + fromModel(item: RegistryModel): ExternalRegistryEditorModel { + this.abbreviation = item.abbreviation; + this.definition = item.definition; + this.id = item.id; + this.label = item.label; + this.reference = item.pid ? item.pid : item.reference; + this.uri = item.uri; + this.source = item.source + + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + return new UntypedFormBuilder().group({ + id: [this.id], + abbreviation: [this.abbreviation], + label: [this.label, Validators.required], + reference: [this.reference], + uri: [this.uri, Validators.required], + definition: [this.definition], + source: [this.source] + }); + } +} + +export class ExternalDatasetEditorModel { + + public abbreviation: String; + public id: String; + public name: String; + public reference: String; + public type: ExternalDatasetType; + public info: String; + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + public source: String; + + constructor(id?: string, abbreviation?: string, name?: string, reference?: string, source?: String, info?: string, type?: ExternalDatasetType) { + this.id = id; + this.name = name; + this.abbreviation = abbreviation; + this.reference = reference; + this.info = info; + this.type = type; + this.source = source; + } + + fromModel(item: ExternalDatasetModel): ExternalDatasetEditorModel { + this.abbreviation = item.abbreviation; + this.id = item.id; + this.name = item.name; + this.reference = item.reference; + this.type = item.type; + this.info = item.info; + this.source = item.source; + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + return new UntypedFormBuilder().group({ + id: [this.id], + abbreviation: [this.abbreviation], + name: [this.name, Validators.required], + reference: [this.reference], + type: [this.type], + info: [this.info], + source: [this.source] + }); + } +} + +export class ExternalDataRepositoryEditorModel { + public id: string; + public name: string; + public abbreviation: string; + public uri: string; + public reference: string; + public info: string; + public created: Date; + public modified: Date; + public source: string; + + constructor(id?: string, name?: string, abbreviation?: string, uri?: string, reference?: string, source?: string) { + this.id = id; + this.name = name; + this.abbreviation = abbreviation; + this.uri = uri; + this.reference = reference; + this.source = source; + } + + fromModel(item: DataRepositoryModel): ExternalDataRepositoryEditorModel { + this.id = item.id; + this.name = item.name; + this.abbreviation = item.abbreviation; + this.uri = item.uri; + this.info = item.info; + this.reference = item.pid; + this.source = item.source; + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + return new UntypedFormBuilder().group({ + id: [this.id], + name: [this.name, [Validators.required]], + abbreviation: [this.abbreviation], + uri: [this.uri, [Validators.required]], + info: [this.info], + reference: [this.reference], + source: [this.source] + }); + } +} + +// export class TagModel implements Serializable { + +// public id: string; +// public name: string; + +// constructor(id?: string, name?: string) { +// this.id = id; +// this.name = name; +// } + +// fromJSONObject(item: any): TagModel { +// this.id = item.id; +// this.name = item.name; +// return this; +// } + +// buildForm(context: ValidationContext = null, disabled: boolean = false): FormGroup { +// return new FormBuilder().group({ +// id: [this.id], +// name: [this.name] +// }); +// } +// } diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.html new file mode 100644 index 000000000..6f65a8697 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.html @@ -0,0 +1,288 @@ +
+
+
+ + +
+
+
+
+
+ +
{{'DMP-EDITOR.TITLE.ADD-DATASET' | translate}}
+
{{'DMP-EDITOR.TITLE.EDIT-DESCRIPTION' | translate}}
+
{{ formGroup.get('label').value }} ({{'DMP-EDITOR.CHANGES' | translate}})
+
+ +
{{'DMP-EDITOR.TITLE.PREVIEW-DATASET' | translate}}
+
+
+
{{'DATASET-LISTING.TOOLTIP.TO-DMP' | translate}}
+
: {{ formGroup.get('dmp').value.label }}
+ +
+
+
+
+ + + + + + +
+ + + +
+ +
+
+ + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ chevron_left + {{'DATASET-WIZARD.ACTIONS.BACK-TO' | translate}} +
+
{{'DATASET-LISTING.TOOLTIP.DMP' | translate}}
+
+
{{'DMP-EDITOR.STEPPER.USER-GUIDE' | translate}}
+
+
+
0. {{'DMP-EDITOR.STEPPER.MAIN-INFO' | translate}} (2)
+
0. {{'DMP-EDITOR.STEPPER.MAIN-INFO' | translate}} (done)
+
+
+ +
+
+
+
+ + +
+
{{'DMP-EDITOR.STEPPER.NEXT' | translate}}
+ chevron_right +
+
+
{{'DMP-EDITOR.STEPPER.NEXT' | translate}}
+ chevron_right +
+ +
+
+ +
+
+
+ + +
+
+
+
+
+ + + + + + diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.scss new file mode 100644 index 000000000..246e7ad8b --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.scss @@ -0,0 +1,524 @@ +.main-content { + height: 100vh !important; + margin-top: -80px; +} + +.dataset-wizard { + .toc-pane-container { + &.is-sticky ~ .nav-spacer { + height: 500px; // the container size } + // height: calc(100vh - 100px); // the container size } + } + } + + .step-container { + margin-top: 1em; + } + + .external-item-card { + margin-top: 1em; + } + + .external-item-action-row, + .description-action-row { + margin-top: 1em; + } + + .deleteButton, + .reverseButton { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + text-transform: uppercase; + } + + .cancelButton { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + text-transform: uppercase; + } + + .saveButton { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + text-transform: uppercase; + } + + .saveAndFinalizeButton { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + } + + .finalizeButton { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + text-transform: uppercase; + } + + .export-btn { + padding-right: 6px; + } + + .downloadPDF { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + } + + .downloadXML { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + } + + .downloadDOCX { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + } + + .updateDatasetProfile { + margin-top: 15px; + margin-bottom: 15px; + margin-right: 15px; + } + + .actions { + display: flex; + justify-content: flex-end; + } + + // .actions > button { + // background-color: #0070c0; + // color: #ffffff; + // text-transform: uppercase; + // } + + .more-horiz { + font-size: 28px; + color: #aaaaaa; + } + + .more-icon :hover { + color: var(--primary-color-3); + } + + .new-dataset { + height: 3.5em; + } + + .fixed-editor-header { + // position: fixed; + // width: calc(100% - 310px); + z-index: 3; + background-color: whitesmoke; + } + + .dataset-editor-header { + height: 113px; + background: var(--unnamed-color-var(--primary-color)) 0% 0% no-repeat padding-box; + background: var(--secondary-color) 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #00000029; + padding: 0.6rem; + margin: 30px 0px 0px 0px; + border-radius: 4px; + opacity: 1; + + .info { + flex: 2; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .dataset-title { + text-align: left; + letter-spacing: 0px; + color: #212121; + opacity: 0.9; + font-size: 14px; + font-weight: 400; + } + + .subtitle { + text-align: left; + color: #ffffff; + font-weight: 700; + font-size: 16px; + opacity: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .dataset-subtitle { + text-align: left; + letter-spacing: 0px; + color: #212121; + font-weight: 700; + font-size: 16px; + opacity: 0.9; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .discard-btn { + background: transparent; + border: 1px solid #ffffff; + color: white; + border-radius: 30px; + opacity: 1; + width: 110px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + } + + .save-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border-radius: 30px; + opacity: 1; + width: auto; + min-width: 110px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + font-weight: 700; + color: var(--primary-color); + } + + .dataset-discard-btn { + border: 1px solid #212121; + border-radius: 30px; + opacity: 1; + background-color: transparent; + font-weight: 700; + width: 110px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + } + + .dataset-save-btn, .dataset-export-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border-radius: 30px; + opacity: 1; + width: auto; + min-width: 110px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + } + + .dataset-to-dmp { + display: flex; + align-items: center; + padding: 0; + text-align: left; + font-size: 1rem; + font-weight: 700; + color: var(--primary-color); + line-height: 25px; + } + + .dmp-info { + padding-top: 0.75rem; + } + + .dmp-label { + min-width: 67px; + height: 37px; + background: var(--primary-color) 0% 0% no-repeat padding-box; + color: white; + border-radius: 4px; + opacity: 1; + display: flex; + align-items: center; + justify-content: center; + } + + .dmp-title { + text-align: left; + font-weight: 700; + font-family: "Roboto", sans-serif; + font-size: 1rem; + color: #212121; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 25px; + } + + .open-in-new-icon { + color: #434343; + opacity: 0.75; + } + .open-in-new-icon:hover { + color: var(--primary-color); + } + + .dataset-stepper { + position: fixed; + // height: 100%; + display: flex; + flex-direction: column; + height: calc(100vh - 246px); + // max-width: 366px; + } + + .stepper-options { + height: calc(100vh - 600px); + overflow-y: auto; + .main-info { + padding-left: .2rem; + color: #21212194; + font-weight: 400; + cursor: pointer; + } + .main-info:hover { + background-color: #ececec; + border-radius: 6px; + } + } + + .stepper-back { + margin-top: 3.125rem; + padding-left: 0.5rem; + font-size: 0.875rem; + letter-spacing: 0px; + color: #848484; + opacity: 1; + } + + .stepper-title { + text-align: left; + font-weight: 300; + font-size: 20px; + letter-spacing: 0px; + color: #212121; + opacity: 0.6; + margin: 2.875rem 0rem 2.25rem 0rem; + padding-left: 1rem; + } + + .stepper-list { + .toc-pane-container { + // padding-left: 0.2rem; + // overflow-x: hidden; + span { + text-align: left; + font-weight: 400; + letter-spacing: 0px; + color: #212121; + padding: 0.3rem 0.1rem; + opacity: 0.6; + cursor: pointer; + max-width: 290px; + } + } + } + + .stepper-list span:hover { + background-color: #ececec; + border-radius: 6px; + } + + .stepper-list .active { + color: #212121; + font-weight: 700; + opacity: 1; + } + + .stepper-list .active-dataset { + color: #212121; + font-weight: 700; + opacity: 1; + .label { + width: 100%; + height: 27px; + line-height: 27px; + background-color: var(--secondary-color); + color: #5d5d5d; + border-radius: 4px; + font-weight: 400; + font-size: 14px; + justify-content: left; + display: flex; + align-items: center; + padding-left: 0.625rem; + padding-right: 0.625rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + } + } + + .back-to-dmp:hover { + background: #ececec; + border-radius: 6px; + } + + .back-icon { + display: inline-flex; + vertical-align: middle; + } + + .editor-content { + .form { + // position: relative; + // left: 362px; + // width: calc(100% - 366px); + + position: relative; + left: 362px; + width: calc(100% - 366px); + overflow-y: auto; + height: calc(100vh - 218px); + } + } + + form { + height: calc(100vh - 124px); + margin-top: 6rem; + } + + .stepper-actions { + display: flex; + padding-left: 1rem; + margin-top: auto; + margin-bottom: 0.5rem; + // margin-top: 5rem; + // flex-grow: 8; + } + + .stepper-btn { + border-radius: 30px; + opacity: 1; + width: 154px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + font-size: 14px; + } + + .previous { + color: #212121; + background: #f5f5f5 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1e202029; + border: 2px solid #212121; + font-weight: 500; + cursor: pointer; + } + + .add-dataset-btn { + background: var(--secondary-color) 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1e202029; + font-weight: 500; + word-wrap: break-word; + white-space: normal; + line-height: normal; + } + + .next { + background: var(--primary-color) 0% 0% no-repeat padding-box; + color: white; + box-shadow: 0px 3px 6px #1e202029; + font-weight: 400; + cursor: pointer; + } + + .dataset-next { + background: var(--secondary-color) 0% 0% no-repeat padding-box; + color: #212121; + box-shadow: 0px 3px 6px #1e202029; + font-weight: 700; + cursor: pointer; + } + + .previous-disabled { + border: 1px solid #b5b5b5; + color: #b5b5b5 !important; + cursor: auto !important; + } + + .next-disabled { + background: #cbcbcb 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1e202029; + color: white; + cursor: auto !important; + } +} + +mat-icon.size-18 { + width: 18px; + height: 18px; + line-height: 18px; + font-size: 18px; +} + +.menu-item { + width: 248px; +} + +.toc-pane-container { + &.is-sticky ~ .nav-spacer { + height: 500px; // the container size } + // height: calc(100vh - 100px); // the container size } + } +} + +.is-sticky { + margin-top: 70px !important; +} + +.done-icon { + display: inline-flex; + vertical-align: middle; + font-size: 16px !important; + height: auto; + width: auto; +} + +// ::ng-deep .mat-tab-labels { +// justify-content: space-between; +// } + +// ::ng-deep .mat-tab-label-content { +// text-transform: uppercase; +// } + +// ::ng-deep .mat-ink-bar { +// background-color: var(--primary-color-3) !important; +// // background-color: #0070c0 !important; +// } + +// @media (max-width: 768px) { +// .main-content { +// padding: 30px 0px; +// } + +// ::ng-deep .mat-expansion-panel-header { +// min-height: 48px; +// height: auto !important; +// } + +// ::ng-deep .mat-expansion-panel-body { +// padding: 0 0px 16px !important; +// } + +// ::ng-deep .mat-vertical-content { +// padding: 0 14px 24px 14px !important; +// } +// } diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.ts new file mode 100644 index 000000000..0907aa039 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/dataset-wizard.component.ts @@ -0,0 +1,1507 @@ +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { SaveType } from '@app/core/common/enum/save-type'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; +import { DmpModel } from '@app/core/model/dmp/dmp'; +import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; +import { LockModel } from '@app/core/model/lock/lock.model'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; +import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; +import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; +import { RequestItem } from '@app/core/query/request-item'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { + ExternalSourcesConfigurationService +} from '@app/core/services/external-sources/external-sources-configuration.service'; +import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { + SnackBarNotificationLevel, + UiNotificationService +} from '@app/core/services/notification/ui-notification-service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate.component'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { + DatasetCopyDialogueComponent +} from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; +import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { PrefillDatasetComponent } from "@app/ui/dataset/dataset-wizard/prefill-dataset/prefill-dataset.component"; +// import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; +import { ToCEntry, ToCEntryType } from "@app/ui/misc/dataset-description-form/dataset-description.component"; +import { + Link, + LinkToScroll, + TableOfContents +} from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents'; +import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { FormService } from '@common/forms/form-service'; +import { + FormValidationErrorsDialogComponent +} from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { Observable, interval, of as observableOf } from 'rxjs'; +import { catchError, debounceTime, filter, map, takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'app-dataset-wizard-component', + templateUrl: 'dataset-wizard.component.html', + styleUrls: ['./dataset-wizard.component.scss'] +}) +export class DatasetWizardComponent extends CheckDeactivateBaseComponent implements OnInit {//IBreadCrumbComponent + canDeactivate(): boolean { + return !this.isDirty(); + } + + // breadCrumbs: Observable; + viewOnly = false; + editMode = false; + publicMode = false; + hasChanges = false; + isDiscarded = false; + formGroupRawValue: any; + saving = false; + + DatasetStatus = DatasetStatus; + dmpAutoCompleteConfiguration: SingleAutoCompleteConfiguration; + + datasetWizardModel: DatasetWizardEditorModel; + isNew = true; + isCopy = false; + formGroup: UntypedFormGroup = null; + datasetProfileDefinitionModel: DatasetDescriptionFormEditorModel; + + availableProfiles: DatasetProfileModel[] = []; + finalize: boolean = false; + itemId: string; + dmpId: string; + dmpSectionIndex: number; + availableDescriptionTemplates: DatasetProfileModel[] = []; + newDmpId: string; + publicId: string; + profileUpdateId: string; + downloadDocumentId: string; + isLinear = false; + lock: LockModel; + lockStatus: Boolean; + + step: number = 0; + stepOffset: number = 1; + + saveAnd = SaveType; + datasetSavedLinks: any = null; + + scrollTop: number; + tocScrollTop: number; + links: Link[] = []; + //the table seraches for elements to scroll on page with id (TOCENTRY_ID_PREFIX+fieldsetId) + TOCENTRY_ID_PREFIX = "TocEntRy"; + showtocentriesErrors = false; + @ViewChild('table0fContents') table0fContents: TableOfContents; + hintErrors: boolean = false; + datasetIsOnceSaved = false; + + fieldsetIdWithFocus: string; + visRulesService: VisibilityRulesService; + + constructor( + private datasetWizardService: DatasetWizardService, + private route: ActivatedRoute, + public snackBar: MatSnackBar, + public router: Router, + public language: TranslateService, + public externalSourcesService: ExternalSourcesService, + public dmpService: DmpService, + public dialog: MatDialog, + public externalSourcesConfigurationService: ExternalSourcesConfigurationService, + private uiNotificationService: UiNotificationService, + private formService: FormService, + private lockService: LockService, + private location: Location, + private authService: AuthService, + private configurationService: ConfigurationService, + private httpClient: HttpClient, + private matomoService: MatomoService, + private fileUtils: FileUtils + ) { + super(); + } + + ngOnInit() { + this.matomoService.trackPageView('Dataset Editor'); + this.route + .data + .pipe(takeUntil(this._destroyed)) + .subscribe(v => { + this.viewOnly = v['public']; + }); + + const dmpRequestItem: RequestItem = new RequestItem(); + dmpRequestItem.criteria = new DmpCriteria(); + + this.dmpAutoCompleteConfiguration = { + filterFn: this.searchDmp.bind(this), + initialItems: (extraData) => this.searchDmp(''), + displayFn: (item) => this.getDatasetDisplay(item), + titleFn: (item) => item['label'], + subtitleFn: (item) => this.language.instant('DATASET-WIZARD.FIRST-STEP.SUB-TITLE') + new Date(item['creationTime']).toISOString() + // iconFn: (item) => this.publicMode ? '' : (item['status'] ? 'lock' : 'lock_open'), + // linkFn: (item) => this.publicMode ? '/explore-plans/overview/' + item['id'] : '/plans/overview/' + item['id'] + }; + + const params = this.route.snapshot.params; + const queryParams = this.route.snapshot.queryParams; + const data: any = this.route.snapshot.data; + this.itemId = params['id']; + this.dmpId = params['dmpId']; + this.dmpSectionIndex = parseInt(params['dmpSectionIndex']); + this.newDmpId = queryParams['newDmpId']; + this.publicId = params['publicId']; + this.profileUpdateId = params['updateId']; + this.finalize = data.finalize; + this.itemId ? this.downloadDocumentId = this.itemId : this.downloadDocumentId = this.publicId + + this.init(); + // this.route.params + // .pipe(takeUntil(this._destroyed)) + // .subscribe((params: Params) => { + // const itemId = params['id']; + // if (itemId != null) { setTimeout(() => this.stepper.selectedIndex = 2); } + // }); + } + + init() { + if (this.itemId != null && this.newDmpId == null) { + this.isNew = false; + this.datasetWizardService.getSingle(this.itemId) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.lockService.checkLockStatus(data.id).pipe(takeUntil(this._destroyed)).subscribe(lockStatus => { + this.lockStatus = lockStatus; + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.dmpSectionIndex = this.datasetWizardModel.dmpSectionIndex; + this.needsUpdate(); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.datasetWizardModel.label, + // url: '/datasets/edit/' + this.datasetWizardModel.id, + // notFoundResolver: [ + // { + // parentComponentName: null, + // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS').toUpperCase(), + // url: '/datasets' + // }, + // ] + // }]); + this.formGroup = this.datasetWizardModel.buildForm(); + let profiles = this.datasetWizardModel.dmp.profiles.filter(profile => profile.data.dmpSectionIndex.includes(this.datasetWizardModel.dmpSectionIndex)); + for (var profile of profiles) { + this.availableDescriptionTemplates.push({ id: profile.descriptionTemplateId, label: profile.label, description: "" }) + } + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + if (this.datasetWizardModel.status === DatasetStatus.Finalized || lockStatus) { + this.formGroup.disable(); + this.viewOnly = true; + } + if (!lockStatus && !isNullOrUndefined(this.authService.currentAccountIsAuthenticated())) { + const lockedBy: UserInfoListingModel = { + email: this.authService.getUserProfileEmail(), + id: this.authService.userId()?.toString(), + name: this.authService.getPrincipalName(), + role: 0 //TODO + //role: this.authService.getRoles()?.at(0) + } + this.lock = new LockModel(data.id, lockedBy); + + this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => { + this.lock.id = Guid.parse(result); + interval(this.configurationService.lockInterval).pipe(takeUntil(this._destroyed)).subscribe(() => this.pumpLock()); + }); + } + // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + this.loadDatasetProfiles(); + this.registerFormListeners(); + + if (lockStatus) { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DATASET-WIZARD.LOCKED.TITLE'), + message: this.language.instant('DATASET-WIZARD.LOCKED.MESSAGE') + }, maxWidth: '30em' + }); + } + if (this.finalize && !this.lockStatus && !this.viewOnly) { + setTimeout(() => { + this.saveFinalize(); + }, 0); + } + // this.availableProfiles = this.datasetWizardModel.dmp.profiles; + }); + }, + error => { + switch (error.status) { + case 403: + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-WIZARD.MESSAGES.DATASET-NOT-ALLOWED'), SnackBarNotificationLevel.Error); + break; + case 404: + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-WIZARD.MESSAGES.DATASET-NOT-FOUND'), SnackBarNotificationLevel.Error); + break; + default: + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.ERRORS.HTTP-REQUEST-ERROR'), SnackBarNotificationLevel.Error); + } + this.router.navigate(['/datasets/']); + return observableOf(null); + }); + } else if (this.dmpId != null) { + this.isNew = true; + this.dmpService.getSingle(this.dmpId).pipe(map(data => data as DmpModel)) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.datasetWizardModel = new DatasetWizardEditorModel(); + setTimeout(() => { + this.datasetWizardModel.dmp = data; + this.datasetWizardModel.dmpSectionIndex = this.dmpSectionIndex; + this.formGroup = this.datasetWizardModel.buildForm(); + let profiles = this.datasetWizardModel.dmp.profiles.filter(profile => profile.data.dmpSectionIndex.includes(this.dmpSectionIndex)); + for (var profile of profiles) { + this.availableDescriptionTemplates.push({ id: profile.descriptionTemplateId, label: profile.label, description: "" }) + } + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + this.formGroup.get('dmp').disable(); + const dialogRef = this.dialog.open(PrefillDatasetComponent, { + width: '590px', + minHeight: '200px', + restoreFocus: false, + data: { + availableProfiles: this.availableDescriptionTemplates, + datasetFormGroup: this.formGroup + }, + panelClass: 'custom-modalbox' + }); + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.datasetWizardModel = this.datasetWizardModel.fromModel(result); + this.datasetWizardModel.dmp = data; + this.datasetWizardModel.dmpSectionIndex = this.dmpSectionIndex; + this.formGroup = this.datasetWizardModel.buildForm(); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + this.formGroup.get('dmp').disable(); + this.loadDatasetProfiles(); + this.registerFormListeners(); + } + }) + this.loadDatasetProfiles(); + this.registerFormListeners(); + // this.availableProfiles = data.profiles; + + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), + // url: '/datasets', + // notFoundResolver: [ + // // { + // // parentComponentName: null, + // // label: this.datasetWizardModel.dmp.grant.label, + // // url: '/grants/edit/' + this.datasetWizardModel.dmp.grant.id + // // }, + // { + // parentComponentName: null, + // label: this.datasetWizardModel.dmp.label, + // url: '/plans/edit/' + this.datasetWizardModel.dmp.id, + // }] + // }]); + }); + }); + } else if (this.newDmpId != null) { + this.isNew = false; + this.isCopy = true; + this.datasetWizardService.getSingle(this.itemId) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.lockService.checkLockStatus(data.id).pipe(takeUntil(this._destroyed)).subscribe(lockStatus => { + this.lockStatus = lockStatus; + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.dmpSectionIndex = this.datasetWizardModel.dmpSectionIndex; + this.datasetWizardModel.status = 0; + this.formGroup = this.datasetWizardModel.buildForm(); + this.formGroup.get('id').setValue(null); + this.dmpService.getSingleNoDatasets(this.newDmpId).pipe(map(data => data as DmpModel)) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + setTimeout(() => { + this.datasetWizardModel.dmp = data; + this.formGroup.get('dmp').setValue(this.datasetWizardModel.dmp); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + + this.loadDatasetProfiles(); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), + // url: '/datasets', + // notFoundResolver: [ + // // { + // // parentComponentName: null, + // // label: this.datasetWizardModel.dmp.grant.label, + // // url: '/grants/edit/' + this.datasetWizardModel.dmp.grant.id + // // }, + // { + // parentComponentName: null, + // label: this.datasetWizardModel.dmp.label, + // url: '/plans/edit/' + this.datasetWizardModel.dmp.id, + // } + // ] + // }]); + }); + }); + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + if (this.datasetWizardModel.status === DatasetStatus.Finalized || lockStatus) { + this.formGroup.disable(); + this.viewOnly = true; + } + if (!lockStatus && !isNullOrUndefined(this.authService.currentAccountIsAuthenticated())) { + const lockedBy: UserInfoListingModel = { + email: this.authService.getUserProfileEmail(), + id: this.authService.userId()?.toString(), + name: this.authService.getPrincipalName(), + role: 0 //TODO + //role: this.authService.getRoles()?.at(0) + } + this.lock = new LockModel(data.id, lockedBy); + + this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => { + this.lock.id = Guid.parse(result); + interval(this.configurationService.lockInterval).pipe(takeUntil(this._destroyed)).subscribe(() => this.pumpLock()); + }); + } + // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + this.loadDatasetProfiles(); + // this.availableProfiles = data.dmp.profiles; + }) + }); + } else if (this.publicId != null) { // For Finalized -> Public Datasets + this.isNew = false; + this.datasetWizardService.getSinglePublic(this.publicId) + .pipe(takeUntil(this._destroyed)).pipe( + catchError((error: any) => { + this.uiNotificationService.snackBarNotification(error.error.message, SnackBarNotificationLevel.Error); + this.router.navigate(['/datasets/publicEdit/' + this.publicId]); + return observableOf(null); + })) + .subscribe(data => { + if (data) { + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.formGroup = this.datasetWizardModel.buildForm(); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + this.formGroup.disable(); + this.viewOnly = true; + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + this.formGroup.get('dmp').setValue(this.datasetWizardModel.dmp); + const breadcrumbs = []; + breadcrumbs.push({ + parentComponentName: null, + label: this.language.instant('NAV-BAR.PUBLIC DATASETS'), + url: '/explore' + }); + breadcrumbs.push({ + parentComponentName: null, + label: this.datasetWizardModel.label, + url: '/datasets/publicEdit/' + this.datasetWizardModel.id + }); + // this.breadCrumbs = observableOf(breadcrumbs); + } + }); + this.publicMode = true; + } else if (this.profileUpdateId != null) { + this.datasetWizardService.updateDatasetProfile(this.profileUpdateId) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + + this.needsUpdate(); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), + // url: '/datasets', + // notFoundResolver: [ + // // { + // // parentComponentName: null, + // // label: this.datasetWizardModel.dmp.grant.label, + // // url: '/grants/edit/' + this.datasetWizardModel.dmp.grant.id + // // }, + // { + // parentComponentName: null, + // label: this.datasetWizardModel.dmp.label, + // url: '/plans/edit/' + this.datasetWizardModel.dmp.id, + // }, + // ] + // }]); + this.formGroup = this.datasetWizardModel.buildForm(); + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + if (this.datasetWizardModel.status === DatasetStatus.Finalized) { + this.formGroup.disable(); + this.viewOnly = true; + } + // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + this.loadDatasetProfiles(); + }); + + } else { + this.datasetWizardModel = new DatasetWizardEditorModel(); + this.formGroup = this.datasetWizardModel.buildForm(); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + if (this.datasetWizardModel.status === DatasetStatus.Finalized) { + this.formGroup.disable(); + this.viewOnly = true; + } + //if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + this.registerFormListeners(); + this.dmpValueChanged(null); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('DATASET-LISTING.ACTIONS.CREATE-NEW').toUpperCase(), + // url: '/datasets/new/' + // }]); + } + } + + // private _listenersSubscription:Subscription = new Subscription(); + registerFormListeners() { + // const dmpSubscription = + this.formGroup.get('dmp').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.dmpValueChanged(x); + }); + // const profileSubscription = + this.formGroup.get('profile').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + if (x) { + this.showtocentriesErrors = false; + this.datasetProfileValueChanged(x.id); + this.formChanged(); + } + }); + // const labelSubscription = + this.formGroup.get('label').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + // const descriptionSubscription = + this.formGroup.get('description').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + // const uriSubscription = + this.formGroup.get('uri').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + // const tagsSubscription = + this.formGroup.get('tags').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + if (this.formGroup.get('datasetProfileDefinition')) { + // const datasetProfileDefinitionSubscription = + this.formGroup.get('datasetProfileDefinition').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + // this._listenersSubscription.add(datasetProfileDefinitionSubscription); + } + + // this._listenersSubscription.add(dmpSubscription); + // this._listenersSubscription.add(profileSubscription); + // this._listenersSubscription.add(labelSubscription); + // this._listenersSubscription.add(descriptionSubscription); + // this._listenersSubscription.add(uriSubscription); + // this._listenersSubscription.add(tagsSubscription); + } + + // private _unregisterFormListeners(){ + // this._listenersSubscription.unsubscribe(); + // this._listenersSubscription = new Subscription(); + // } + + dmpValueChanged(dmp: DmpListingModel) { + if (dmp) { + this.formGroup.get('profile').enable(); + this.loadDatasetProfiles(); + } else { + this.availableProfiles = []; + this.formGroup.get('profile').reset(); + this.formGroup.get('profile').disable(); + this.formGroup.removeControl('datasetProfileDefinition'); + } + } + + datasetProfileValueChanged(profiledId: string) { + if (profiledId && profiledId.length > 0) { + this.formGroup.removeControl('datasetProfileDefinition'); + this.getDefinition(profiledId); + } + } + + searchDmp(query: string): Observable { + const fields: Array = new Array(); + fields.push('-created'); + const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + dmpDataTableRequest.criteria = new DmpCriteria(); + dmpDataTableRequest.criteria.like = query; + dmpDataTableRequest.criteria.status = DmpStatus.Draft; + return this.dmpService.getPaged(dmpDataTableRequest, "autocomplete").pipe(map(x => x.data)); + } + + loadDatasetProfiles() { + const datasetProfileRequestItem: RequestItem = new RequestItem(); + datasetProfileRequestItem.criteria = new DatasetProfileCriteria(); + datasetProfileRequestItem.criteria.id = this.formGroup.get('dmp').value.id; + if (datasetProfileRequestItem.criteria.id) { + this.datasetWizardService.getAvailableProfiles(datasetProfileRequestItem) + .pipe(takeUntil(this._destroyed)) + .subscribe(items => { + this.availableProfiles = items; + }); + } + } + + public formChanged() { + if (!this.isDiscarded) { + this.hasChanges = true; + } + } + + public cancel(): void { + if (!isNullOrUndefined(this.lock)) { + this.lockService.unlockTarget(this.datasetWizardModel.id).pipe(takeUntil(this._destroyed)).subscribe( + complete => { + this.publicMode ? this.router.navigate(['/explore']) : this.router.navigate(['/datasets']); + }, + error => { + this.formGroup.get('status').setValue(DmpStatus.Draft); + this.onCallbackError(error); + } + ) + } else { + this.publicMode ? this.router.navigate(['/explore']) : this.router.navigate(['/datasets']); + } + + } + + getDatasetDisplay(item: any): string { + if (!this.publicMode) { + return (item['status'] ? this.language.instant('TYPES.DATASET-STATUS.FINALISED').toUpperCase() : this.language.instant('TYPES.DATASET-STATUS.DRAFT').toUpperCase()) + ': ' + item['label']; + } else { + return item['label']; + } + } + + getDefinition(profileId: string) { + // if (this.formGroup.invalid) { setTimeout(() => this.stepper.selectedIndex = 0); return; } + this.datasetWizardService.getDefinition(profileId) + .pipe(takeUntil(this._destroyed)) + .subscribe(item => { + this.datasetWizardModel.datasetProfileDefinition = new DatasetDescriptionFormEditorModel().fromModel(item); + this.datasetProfileDefinitionModel = this.datasetWizardModel.datasetProfileDefinition; + this.formGroup.addControl('datasetProfileDefinition', this.datasetProfileDefinitionModel.buildForm()); + + // const datasetProfileDefinitionForm = this.datasetProfileDefinitionModel.buildForm(); + + // let profId = null; + // try{ + // profId = this.formGroup.get('profile').value.id; + // }catch{ + + // } + // if(this.formGroupRawValue && this.formGroupRawValue.datasetProfileDefinition && (this.formGroupRawValue.profile.id === profId)){ + // // this.formGroup.get('datasetProfileDefinition').patchValue( this.formGroupRawValue.datasetProfileDefinition); + // datasetProfileDefinitionForm.patchValue(this.formGroupRawValue.datasetProfileDefinition); + // } + + // this.formGroup.addControl('datasetProfileDefinition', datasetProfileDefinitionForm); + this.formGroup.get('datasetProfileDefinition').valueChanges + .pipe(debounceTime(600)) + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.formChanged(); + }); + }); + } + + // formSubmit(): void { + // if (!this.isFormValid()) { return; } + // this.onSubmit(); + // } + + public isFormValid() { + return this.formGroup.valid; + } + + public isSemiFormValid(formGroup: UntypedFormGroup): boolean { + var isValid: boolean = true; + Object.keys(formGroup.controls).forEach(controlName => { + if (controlName != 'datasetProfileDefinition' && !formGroup.get(controlName).disabled && !(formGroup.get(controlName).valid)) { + isValid = false; + } + }); + return isValid; + } + + // onSubmit(): void { + // this.datasetWizardService.createDataset(this.formGroup.value) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // complete => { + // this.datasetWizardService.getSingle(complete.id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // result => { + // this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(result); + // } + // ); + // this.onCallbackSuccess(); + // }, + // error => this.onCallbackError(error) + // ); + // } + + + submit(saveType?: SaveType, onSuccess: Function = null, onError: Function = null) { + this.scrollTop = document.getElementById('dataset-editor-form').scrollTop; + this.tocScrollTop = document.getElementById('stepper-options').scrollTop; + this.datasetWizardService.createDataset(this.formGroup.getRawValue()) + .pipe(takeUntil(this._destroyed)) + .subscribe( + data => { + this.hasChanges = false; + this.datasetIsOnceSaved = true; + this.onCallbackSuccess(data, saveType); + if (onSuccess) { + onSuccess(); + } + }, + error => { + if (onError) { + onError(); + } + this.onCallbackError(error) + }); + } + + + private _getErrorMessage(formControl: AbstractControl, name: string): string[] { + const errors: string[] = []; + Object.keys(formControl.errors).forEach(key => { + if (key === 'required') { + errors.push(this.language.instant(name + ": " + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED'))); + } + // if (key === 'required') { errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + this.getPlaceHolder(formControl) + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.REQUIRED')); } + else if (key === 'email') { + errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.EMAIL')); + } else if (key === 'min') { + errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MIN-VALUE', { 'min': formControl.getError('min').min })); + } else if (key === 'max') { + errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.MAX-VALUE', { 'max': formControl.getError('max').max })); + } else { + errors.push(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.THIS-FIELD') + ' "' + name + '" ' + this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR') + ', ' + formControl.errors[key].message); + } + }); + return errors; + } + + private _getPlaceHolder(formControl: any): string { + if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea' + || formControl.nativeElement.localName === 'richTextarea') { + return formControl.nativeElement.getAttribute('placeholder'); + } else if (formControl.nativeElement.localName === 'mat-select') { + return formControl.nativeElement.getAttribute('placeholder'); + } else if (formControl.nativeElement.localName === 'app-single-auto-complete') { + return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); + } else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') { + return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); + } + } + + + private _buildSemiFormErrorMessages(): string[] {//not including datasetProfileDefinition + const errmess: string[] = []; + Object.keys(this.formGroup.controls).forEach(controlName => { + if (controlName != 'datasetProfileDefinition' && this.formGroup.get(controlName).invalid) { + errmess.push(...this._buildErrorMessagesForAbstractControl(this.formGroup.get(controlName), controlName)); + } + }) + + return errmess; + } + + // takes as an input an abstract control and gets its error messages[] + private _buildErrorMessagesForAbstractControl(aControl: AbstractControl, controlName: string): string[] { + const errmess: string[] = []; + + if (aControl.invalid) { + + if (aControl.errors) { + //check if has placeholder + if ((aControl).nativeElement !== undefined && (aControl).nativeElement !== null) { + const placeholder = this._getPlaceHolder(aControl); + if (placeholder) { + controlName = placeholder; + } + } + const errorMessage = this._getErrorMessage(aControl, controlName); + + errmess.push(...errorMessage); + } + + /*in case the aControl is FormControl then the it should have provided its error messages above. + No need to check case of FormControl below*/ + + if (aControl instanceof UntypedFormGroup) { + + const fg = aControl as UntypedFormGroup; + //check children + Object.keys(fg.controls).forEach(controlName => { + errmess.push(...this._buildErrorMessagesForAbstractControl(fg.get(controlName), controlName)); + }); + } else if (aControl instanceof UntypedFormArray) { + + const fa = aControl as UntypedFormArray; + + fa.controls.forEach((control, index) => { + errmess.push(...this._buildErrorMessagesForAbstractControl(control, `${controlName} --> ${index + 1}`)); + }); + + } + } + + return errmess; + } + + save(saveType?: SaveType) { + this.saving = true; + Object.keys(this.formGroup.controls).forEach(controlName => { + if (controlName == 'datasetProfileDefinition') { + return; + } + this.formService.touchAllFormFields(this.formGroup.get(controlName)); + }) + + + // this.formService.touchAllFormFields(this.formGroup); + if (!this.isSemiFormValid(this.formGroup)) { + //build messages + const errorMessages = this._buildSemiFormErrorMessages(); + this.showValidationErrorsDialog(undefined, errorMessages); + this.hintErrors = true; + this.saving = false; + return; + } + this.submit(saveType); + } + + private showValidationErrorsDialog(projectOnly?: boolean, errmess?: string[]) { + if (errmess) { + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + errorMessages: errmess, + projectOnly: projectOnly + }, + }); + } else { + + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + formGroup: this.formGroup, + projectOnly: projectOnly + }, + }); + } + + } + + hasReversableStatus(): boolean { + if (this.formGroup.get('dmp').value) { + return (this.formGroup.get('dmp').value.status == DmpStatus.Draft && this.formGroup.get('status').value == DatasetStatus.Finalized); + } else { + return false; + } + } + + hasNotReversableStatus(): boolean { + if (this.formGroup.get('dmp').value && !this.publicMode) { + return (this.formGroup.get('dmp').value.status == DmpStatus.Finalized && this.formGroup.get('status').value == DatasetStatus.Finalized); + } else { + return false; + } + } + + reverse() { + + this.dialog.open(ConfirmationDialogComponent, { + data: { + message: this.language.instant('DATASET-WIZARD.ACTIONS.UNDO-FINALIZATION-QUESTION'), + confirmButton: this.language.instant('DATASET-WIZARD.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('DATASET-WIZARD.ACTIONS.REJECT'), + }, + maxWidth: '30em' + }) + .afterClosed() + .pipe( + filter(x => x), + takeUntil(this._destroyed) + ).subscribe(result => { + if (result) { + // if (!this.isFormValid()) { return; } + this.formGroup.get('status').setValue(DatasetStatus.Draft); + this.submit(SaveType.finalize, () => { + this.viewOnly = false; + this.datasetWizardModel.status = DatasetStatus.Draft; + setTimeout(x => { + this.formGroup = null; + }); + setTimeout(x => { + this.formGroup = this.datasetWizardModel.buildForm(); + this.registerFormListeners(); + }); + }, () => { + this.formGroup.get('status').setValue(DatasetStatus.Finalized); + this.viewOnly = true; + }); + } else { + this.saving = false; + } + }); + + + } + + saveFinalize() { + // this.formService.touchAllFormFields(this.formGroup); + this.saving = true; + if (!this.isSemiFormValid(this.formGroup) || (this.table0fContents && this.table0fContents.hasVisibleInvalidFields())) { + // this.showValidationErrorsDialog(); + this.dialog.open(FormValidationErrorsDialogComponent, { + data: { + errorMessages: [this.language.instant('DATASET-WIZARD.MESSAGES.MISSING-FIELDS')] + } + }) + + + this.touchForm(); + this.hintErrors = true; + this.saving = false; + return; + } + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.FINALIZE-ITEM'), + confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), + cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), + isDeleteConfirmation: false + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + // if (!this.isFormValid()) { return; } + this.formGroup.get('status').setValue(DatasetStatus.Finalized); + this.submit(SaveType.finalize, null, () => { + this.formGroup.get('status').setValue(DatasetStatus.Draft); + }); + } else { + this.saving = false; + } + }); + } + + onCallbackSuccess(data?: DatasetWizardModel, saveType?: SaveType): void { + this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); + if (data) { + if (saveType === this.saveAnd.addNew) { + this.router.navigate(['/reload']).then(() => { + this.router.navigate(['/datasets', 'new', this.formGroup.get('dmp').value.id, this.dmpSectionIndex]); + }) + } else if (saveType === this.saveAnd.close) { + this.router.navigate(['/reload']).then(() => { + this.router.navigate(['/plans', 'edit', this.formGroup.get('dmp').value.id]); + }); + } else if (saveType === SaveType.finalize) { + this.router.navigate(['/reload']).then(() => { + this.router.navigate(['/datasets', 'edit', data.id]); + }); + } else { + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + // setTimeout(() => { this.formGroup = null; }); + setTimeout(() => { + this.formGroup.get('id').patchValue(data.id); + this.formGroup.get('modified').patchValue(data.modified); + this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + this.hasChanges = false; + + // this.formGroup = this.datasetWizardModel.buildForm(); + // if (this.formGroup.get('datasetProfileDefinition')) { + // this.formGroup.removeControl('datasetProfileDefinition'); + // this.getDefinition(data.profile.id); + // this.maxStep = 1; + // } else { + // this.getDefinition(data.profile.id); + // this.maxStep = 1; + // } + }); + + setTimeout(() => { + document.getElementById('dataset-editor-form').scrollTop = this.scrollTop; + document.getElementById('stepper-options').scrollTop = this.tocScrollTop; + }, 500); + this.saving = false; + if (this.isNew) { + this.reloadDateset(this.datasetWizardModel.id); + } + // this.router.navigate(['/reload']).then(() => { this.router.navigate(['/datasets', 'edit', data.id]); }); + } + } else { + this.router.navigate(['/datasets']); + } + } + + onCallbackError(error: any) { + const errmes = error && error.message ? error.message as string : null; + let feedbackMessage = this.language.instant('DATASET-EDITOR.ERRORS.ERROR-OCCURED'); + if (errmes) { + feedbackMessage += errmes; + } + this.uiNotificationService.snackBarNotification(feedbackMessage, SnackBarNotificationLevel.Warning); + this.setErrorModel(error.error); + this.saving = false; + } + + public setErrorModel(validationErrorModel: ValidationErrorModel) { + Object.keys(validationErrorModel).forEach(item => { + (this.datasetWizardModel.validationErrorModel)[item] = (validationErrorModel)[item]; + }); + } + + downloadPDF(id: string): void { + this.datasetWizardService.downloadPDF(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/pdf' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "pdf", id); + }); + } + + downloadDOCX(id: string): void { + this.datasetWizardService.downloadDOCX(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/msword' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "docx", id); + }); + + } + + downloadXML(id: string): void { + this.datasetWizardService.downloadXML(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "xml", id); + }); + } + + // advancedClicked() { + // const dialogRef = this.dialog.open(ExportMethodDialogComponent, { + // maxWidth: '500px', + // data: { + // message: "Download as:", + // XMLButton: "XML", + // documentButton: "Document", + // pdfButton: "PDF" + // } + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result == "pdf") { + // this.downloadPDF(); + // } else if (result == "xml") { + // this.downloadXML(); + // } else if (result == "doc") { + // this.downloadDOCX(); + // } + // }); + // } + + public redirectToGrant() { + this.router.navigate(['grants/edit/' + this.datasetWizardModel.dmp.grant.id]); + } + + public redirectToDmp() { + this.router.navigate(['plans/edit/' + this.datasetWizardModel.dmp.id]); + } + + public enableForm() { + if (this.formGroup.get('status').value !== DatasetStatus.Finalized) { + this.editMode = true; + this.viewOnly = false; + this.formGroup.enable(); + } else { + this.datasetWizardService.unlock(this.formGroup.get('id').value) + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + this.editMode = true; + this.viewOnly = false; + this.datasetWizardModel.status = DatasetStatus.Draft; + this.formGroup.get('status').patchValue(DatasetStatus.Draft); + this.formGroup.enable(); + }); + } + } + + public disableForm() { + this.editMode = false; + //this.viewOnly = true; + this.formGroup.disable(); + } + + openConfirm(dmpLabel, id): void { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.DELETE'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.datasetWizardService.delete(id) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + + openDmpSearchDialogue() { + const formControl = new UntypedFormControl(); + const dialogRef = this.dialog.open(DatasetCopyDialogueComponent, { + width: '500px', + restoreFocus: false, + data: { + formControl: formControl, + datasetId: this.formGroup.value.id, + datasetProfileId: this.formGroup.value.profile, + datasetProfileExist: false, + confirmButton: this.language.instant('DATASET-WIZARD.DIALOGUE.COPY'), + cancelButton: this.language.instant('DATASET-WIZARD.DIALOGUE.CANCEL') + } + }); + + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (result && result.datasetProfileExist) { + const newDmpId = result.formControl.value.id + this.router.navigate(['/datasets/copy/' + result.datasetId], { queryParams: { newDmpId: newDmpId } }); + } + }); + } + + needsUpdate() { + if (this.datasetWizardModel.isProfileLatestVersion || (this.datasetWizardModel.status === DatasetStatus.Finalized) + || (this.datasetWizardModel.isProfileLatestVersion == undefined && this.datasetWizardModel.status == undefined)) { + return false; + } else { + return true; + } + } + + linkToScroll: LinkToScroll; + + onStepFound(linkToScroll: LinkToScroll) { + this.linkToScroll = linkToScroll; + } + + private pumpLock() { + this.lock.touchedAt = new Date(); + this.lockService.createOrUpdate(this.lock).pipe(takeUntil(this._destroyed)).subscribe(async result => { + if (!isNullOrUndefined(result)) { + this.lock.id = Guid.parse(result); + } else { + this.location.back(); + } + }); + } + + getEntryVisibleFieldSets(entry: ToCEntry): ToCEntry[] { + let fieldSets = []; + if (entry.type === ToCEntryType.FieldSet && !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id)) { + fieldSets.push(entry); + } else if (entry.type !== ToCEntryType.FieldSet) { + entry.subEntries.forEach(subEntry => { + fieldSets = fieldSets.concat(this.getEntryVisibleFieldSets(subEntry)); + }); + } + return fieldSets; + } + + get visibleFieldSets(): ToCEntry[] { + let fieldSets = []; + let arrays = this.table0fContents ? this.table0fContents.tocentries. + filter(entry => !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id)).map(entry => { + return this.getEntryVisibleFieldSets(entry); + }) + : []; + arrays.forEach(array => { + fieldSets = fieldSets.concat(array); + }); + return fieldSets; + } + + getFirstFieldSet(entry: ToCEntry): ToCEntry { + if (entry.type === ToCEntryType.FieldSet && !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === entry.id)) { + return entry; + } else { + let subEntries = entry.subEntries.filter(subEntry => !this.table0fContents.internalTable.hiddenEntries.find(hiddenEntry => hiddenEntry === subEntry.id)); + if (subEntries.length > 0) { + return this.getFirstFieldSet(subEntries[0]); + } else { + return null; + } + } + } + + public changeStep(selected: ToCEntry = null, execute: boolean = true) { + if (execute) { + if (selected) { + let fieldSet = this.getFirstFieldSet(selected); + let index = this.visibleFieldSets.findIndex(entry => entry.id === fieldSet.id); + this.step = index + (selected.type === ToCEntryType.FieldSet ? 1 : 0.5); + } else { + this.step = 0; + this.resetScroll(); + } + } + } + + get maxStep() { + return this.visibleFieldSets.length; + } + + public nextStep() { + if (this.step < this.maxStep) {//view is changing + this.step = Math.floor(this.step + 1); + let entry = this.visibleFieldSets[this.step - 1]; + this.table0fContents.onToCentrySelected(entry, false); + this.scroll(entry); + } + } + + public previousStep() { + if (this.step > 0) { + this.step = Math.ceil(this.step - 1); + if (this.step >= 1) { + let entry = this.visibleFieldSets[this.step - 1]; + this.table0fContents.onToCentrySelected(entry, false); + this.scroll(entry); + } else { + this.table0fContents.onToCentrySelected(null, false); + this.resetScroll(); + } + } + } + + private resetScroll() { + document.getElementById('dataset-editor-form').scrollTop = 0; + } + + private scroll(entry: ToCEntry) { + document.getElementById(entry.id).scrollIntoView(); + } + + isDirty() { + return this.formGroup.dirty && this.hasChanges; // do we need this.formGroup.dirty + } + + discardChanges() { + // this.isDiscarded = true; + // this.hasChanges = false; + // this.hintErrors = false; + let messageText = ""; + let confirmButtonText = ""; + let cancelButtonText = ""; + let isDeleteConfirmation = false; + + if (this.isNew && !this.datasetIsOnceSaved) { + + messageText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-MESSAGE'); + confirmButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-CONFIRM'); + cancelButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-NEW-DENY'); + isDeleteConfirmation = true; + + // Object.keys(this.formGroup['controls']).forEach((key: string) => { + // if (key !== 'dmp' && (key!== 'profile')) { + // if(key === 'datasetProfileDefinition'){ + // this.formGroup.get(key).patchValue(this.datasetProfileDefinitionModel.buildForm().getRawValue); + // }else{ + // this.formGroup.get(key).reset(); + // } + + // } + // }); + } else { + + messageText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-MESSAGE'); + confirmButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-CONFIRM'); + cancelButtonText = this.language.instant('DATASET-EDITOR.ACTIONS.DISCARD.DISCARD-EDITED-DENY'); + isDeleteConfirmation = false; + + // this.isDiscarded = true; + // this.hasChanges = false; + // this.hintErrors = false; + // // this._unregisterFormListeners(); + // this.formGroup.patchValue(JSON.parse(JSON.stringify(this.formGroupRawValue))); + // // this.registerFormListeners(); + // this.isDiscarded = false; + + + } + + + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: messageText, + confirmButton: confirmButtonText, + cancelButton: cancelButtonText, + isDeleteConfirmation: true + }, + maxWidth: '40em' + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + // this.backToDmp(this.formGroup.get('dmp').value.id) + setTimeout(x => { + this.init(); + }); + } + }); + + + // this.isDiscarded = false; + } + + addDataset(dmpId: string) { + this.router.navigate(['/datasets', 'new', dmpId]); + } + + reloadDateset(datasetId: string) { + let url = this.router.createUrlTree(['/datasets', 'edit', datasetId]).toString(); + this.location.go(url); + } + + backToDmp(id: string) { + this.router.navigate(['/plans', 'edit', id]); + } + + datasetInfoValid(): boolean { + return this.formGroup.get('label') && this.formGroup.get('label').valid && this.formGroup.get('profile') && this.formGroup.get('profile').valid; + } + + getLinks(currentLinks: Link[]) { + this.links = currentLinks; + } + + printForm() { + console.log(this.formGroup); + } + + printFormValue() { + console.log(this.formGroup.value); + } + + touchForm() { + this.formGroup.markAllAsTouched(); + this.showtocentriesErrors = true; + } + + // tocentries; + // this.tocentries = this.getTocEntries(this.formGroup.get('datasetProfileDefinition')); //TODO + + + // get tocentries(){ + // const form = this.formGroup.get('datasetProfileDefinition') + // if(!form) return null; + + // return this.getTocEntries(this.formGroup.get('datasetProfileDefinition')); + // } + + + // private _buildRecursively(form: FormGroup,whatAmI:ToCEntryType):ToCEntry{ + // if(!form) return null; + + // switch(whatAmI){ + // case ToCEntryType.Section: + // const sections = form.get('sections') as FormArray; + // const fieldsets = form.get('compositeFields') as FormArray; + + + // const tempResult:ToCEntry[] = []; + + // if(sections &§ions.length){ + // sections.controls.forEach(section=>{ + // tempResult.push(this._buildRecursively(section as FormGroup, ToCEntryType.Section)); + // }); + + // }else if(fieldsets && fieldsets.length){ + // fieldsets.controls.forEach(fieldset=>{ + // tempResult.push(this._buildRecursively(fieldset as FormGroup, ToCEntryType.FieldSet)); + // }); + // } + // return { + // form: form, + // id: form.get('id').value, + // label: form.get('title').value, + // numbering: '', + // subEntries:tempResult, + // subEntriesType: sections &§ions.length? ToCEntryType.Section: ToCEntryType.FieldSet, + // type: ToCEntryType.Section, + // ordinal: form.get('ordinal').value + // } + // case ToCEntryType.FieldSet: + // return { + // form: form, + // label:form.get('title').value, + // id: form.get('id').value, + // numbering:'s', + // subEntries:[], + // subEntriesType: ToCEntryType.Field, + // type: ToCEntryType.FieldSet, + // ordinal: form.get('ordinal').value + // } + // } + // } + + // private _sortByOrdinal(tocentries: ToCEntry[]){ + + // if(!tocentries || !tocentries.length) return; + + // tocentries.sort(this._customCompare); + // tocentries.forEach(entry=>{ + // this._sortByOrdinal(entry.subEntries); + // }); + // } + + // private _customCompare(a,b){ + // return a.ordinal - b.ordinal; + // } + + // private _calculateNumbering(tocentries: ToCEntry[], depth:number[] = []){ + // if(!tocentries || !tocentries.length){ + // return; + // } + + // let prefixNumbering = depth.length? depth.join('.'): ''; + + // if(depth.length) prefixNumbering = prefixNumbering+"."; + // tocentries.forEach((entry,i)=>{ + // entry.numbering = prefixNumbering + (i+1); + // this._calculateNumbering(entry.subEntries, [...depth, i+1]) + // }); + // } + + + // getTocEntries(form): ToCEntry[] { + // if (form == null) { return []; } + // const result: ToCEntry[] = []; + + // //build parent pages + // (form.get('pages') as FormArray).controls.forEach((pageElement, i) => { + // result.push({ + // id: i+'id', + // label: pageElement.get('title').value, + // type: ToCEntryType.Page, + // form: pageElement, + // numbering: (i + 1).toString(), + // subEntriesType: ToCEntryType.Section, + // subEntries:[], + // ordinal: pageElement.get('ordinal').value + // } as ToCEntry) + // }); + + + // result.forEach((entry,i)=>{ + + // const sections = entry.form.get('sections') as FormArray; + + // sections.controls.forEach(section=>{ + // const tempResults = this._buildRecursively(section as FormGroup,ToCEntryType.Section); + // entry.subEntries.push(tempResults); + // }); + + // }); + + // this._sortByOrdinal(result); + // //calculate numbering + // this._calculateNumbering(result); + // return result; + + // } + + +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.html new file mode 100644 index 000000000..4cf8f26cc --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.html @@ -0,0 +1,489 @@ +
+ +
+
+
+

1.3 {{'DATASET-EDITOR.FIELDS.TAGS' | translate}}

+
+
+
+ + + +
+
+ + + + + + + + +
+ + + + diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.scss new file mode 100644 index 000000000..5221d29c7 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.scss @@ -0,0 +1,35 @@ +.dataset-external-references-editor { + .heading { + text-align: left; + font-weight: 700; + font-size: 18px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-top: 1.625rem; + margin-bottom: 0.625rem; + } + + .hint { + text-align: left; + font-weight: 400; + font-size: 16px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-bottom: 2.125rem; + } + + .external-item-card { + margin-bottom: 1em; + } +} + +::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +::ng-deep .tags-form .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.ts new file mode 100644 index 000000000..cac88b15d --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/dataset-external-references-editor.component.ts @@ -0,0 +1,341 @@ +import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; +import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; +import { ExternalSourcesConfiguration } from '@app/core/model/external-sources/external-sources-configuration'; +import { DataRepositoryCriteria } from '@app/core/query/data-repository/data-repository-criteria'; +import { ExternalDatasetCriteria } from '@app/core/query/external-dataset/external-dataset-criteria'; +import { RegistryCriteria } from '@app/core/query/registry/registry-criteria'; +import { RequestItem } from '@app/core/query/request-item'; +import { ServiceCriteria } from '@app/core/query/service/service-criteria'; +import { TagCriteria } from '@app/core/query/tag/tag-criteria'; +import { ExternalSourcesConfigurationService } from '@app/core/services/external-sources/external-sources-configuration.service'; +import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; +import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; +import { ExternalDataRepositoryEditorModel, ExternalDatasetEditorModel, ExternalRegistryEditorModel, ExternalServiceEditorModel, ExternalTagEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { DatasetExternalDataRepositoryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component'; +import { DatasetExternalDatasetDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component'; +import { DatasetExternalRegistryDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component'; +import { DatasetExternalServiceDialogEditorComponent } from '@app/ui/dataset/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component'; +import { BaseComponent } from '@common/base/base.component'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { takeUntil, map } from 'rxjs/operators'; +import { ENTER, COMMA } from '@angular/cdk/keycodes'; +import { MatChipInputEvent } from '@angular/material/chips'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { ExternalDataRepositoryService } from '@app/core/services/external-sources/data-repository/extternal-data-repository.service'; +import { ExternalDatasetService } from '@app/core/services/external-sources/dataset/external-dataset.service'; +import { ExternalRegistryService } from '@app/core/services/external-sources/registry/external-registry.service'; +import { ExternalServiceService } from '@app/core/services/external-sources/service/external-service.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; + +@Component({ + selector: 'app-dataset-external-references-editor-component', + templateUrl: 'dataset-external-references-editor.component.html', + styleUrls: ['./dataset-external-references-editor.component.scss'] +}) +export class DatasetExternalReferencesEditorComponent extends BaseComponent implements OnInit { + + @Input() formGroup: UntypedFormGroup = null; + @Input() viewOnly = false; + @Output() formChanged: EventEmitter = new EventEmitter(); + + public filteringTagsAsync = false; + public filteredTags: ExternalSourceItemModel[]; + + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; + + externalDatasetAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { + filterFn: this.searchDatasetExternalDatasets.bind(this), + initialItems: (type) => this.searchDatasetExternalDatasets('', type),//.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1), + displayFn: (item) => item ? item.name : null, + titleFn: (item) => item ? item.name : null, + subtitleFn: (item) => item.source ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.source : item.tag ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.tag : this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.NO-SOURCE') + }; + + registriesAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { + filterFn: this.searchDatasetExternalRegistries.bind(this), + initialItems: (type) => this.searchDatasetExternalRegistries('', type), + displayFn: (item) => item ? item.name : null, + titleFn: (item) => item ? item.name : null, + subtitleFn: (item) => item.source ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.source : item.tag ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.tag : this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.NO-SOURCE') + }; + + dataRepositoriesAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { + filterFn: this.searchDatasetExternalDataRepositories.bind(this), + initialItems: (type) => this.searchDatasetExternalDataRepositories('', type), + displayFn: (item) => item ? item.name : null, + titleFn: (item) => item ? item.name : null, + subtitleFn: (item) => item.source ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.source : item.tag ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.tag : this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.NO-SOURCE') + }; + + servicesAutoCompleteConfiguration: SingleAutoCompleteConfiguration = { + filterFn: this.searchDatasetExternalServices.bind(this), + initialItems: (type) => this.searchDatasetExternalServices('', type), + displayFn: (item) => item ? item.label : null, + titleFn: (item) => item ? item.label : null, + subtitleFn: (item) => item.source ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.source : item.tag ? this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.SOURCE:') + item.tag : this.language.instant('TYPES.EXTERNAL-DATASET-TYPE.NO-SOURCE') + }; + + tagsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterTags.bind(this), + initialItems: (excludedItems: any[]) => this.filterTags('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => this.showTag(item), + titleFn: (item) => item['name'], + valueAssign: (item) => this.addTag(item) + }; + + externalSourcesConfiguration: ExternalSourcesConfiguration; + + // new AutoCompleteConfiguration(this.externalSourcesService.searchDatasetRepository.bind(this.externalSourcesService), + + constructor( + private dialog: MatDialog, + private router: Router, + private language: TranslateService, + private externalSourcesService: ExternalSourcesService, + private externalSourcesConfigurationService: ExternalSourcesConfigurationService, + private externalDataRepositoryService: ExternalDataRepositoryService, + private externalDatasetService: ExternalDatasetService, + private externalRegistryService: ExternalRegistryService, + private externalServiceService: ExternalServiceService, + ) { super(); } + + ngOnInit() { + + this.externalSourcesConfigurationService.getExternalSourcesConfiguration() + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + this.externalSourcesConfiguration = result; + this.externalSourcesConfiguration.dataRepositories.push({ key: '', label: 'All' }); + this.externalSourcesConfiguration.externalDatasets.push({ key: '', label: 'All' }); + this.externalSourcesConfiguration.registries.push({ key: '', label: 'All' }); + this.externalSourcesConfiguration.services.push({ key: '', label: 'All' }); + if (!isNullOrUndefined(this.externalSourcesConfiguration.tags)) { + this.externalSourcesConfiguration.tags.push({ key: '', label: 'All' }); + } else { + this.externalSourcesConfiguration.tags = [{ key: '', label: 'All' }]; + } + }); + + this.formGroup.valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(val => { + this.formChanged.emit(val); + }); + } + + public cancel(): void { + this.router.navigate(['/datasets']); + } + + externalDatasetsOnItemChange(event) { + const externalDatasetModel = new ExternalDatasetEditorModel(event.id, event.abbreviation, event.name, event.pid ? event.pid : event.reference, event.source); + (this.formGroup.get('externalDatasets')).push(externalDatasetModel.buildForm()); + } + + registriesOnItemChange(event) { + const registryModel = new ExternalRegistryEditorModel(event.abbreviation, event.definition, event.id, event.name, event.pid ? event.pid : event.reference, event.uri, event.source); + (this.formGroup.get('registries')).push(registryModel.buildForm()); + } + + servicesOnItemChange(event) { + const serviceModel = new ExternalServiceEditorModel(event.abbreviation, event.definition, event.id, event.label, event.reference, event.uri); + (this.formGroup.get('services')).push(serviceModel.buildForm()); + } + + tagsOnItemChange(event) { + const tagModel = new ExternalTagEditorModel(event.id, event.name); + (this.formGroup.get('tags')).push(tagModel.buildForm()); + } + + + dataRepositoriesOnItemChange(event) { + const dataRepositoryModel = new ExternalDataRepositoryEditorModel(event.id, event.name, event.abbreviation, event.uri, event.pid, event.source); + (this.formGroup.get('dataRepositories')).push(dataRepositoryModel.buildForm()); + } + + addDataRepository() { + const dialogRef = this.dialog.open(DatasetExternalDataRepositoryDialogEditorComponent, { + width: '500px', + restoreFocus: false, + data: {} + }); + dialogRef.afterClosed() + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return; } + const dataRepositoryModel = new ExternalDataRepositoryEditorModel(result.id, result.name, result.abbreviation, result.uri, result.pid, result.source); + (this.formGroup.get('dataRepositories')).push(dataRepositoryModel.buildForm()); + }); + } + + addRegistry() { + const dialogRef = this.dialog.open(DatasetExternalRegistryDialogEditorComponent, { + width: '500px', + restoreFocus: false, + data: {} + }); + dialogRef.afterClosed() + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return; } + const registryModel = new ExternalRegistryEditorModel(result.abbreviation, result.definition, result.id, result.label, result.reference, result.uri, result.source); + (this.formGroup.get('registries')).push(registryModel.buildForm()); + }); + } + + addExternalDataset() { + const dialogRef = this.dialog.open(DatasetExternalDatasetDialogEditorComponent, { + width: '500px', + restoreFocus: false, + data: {} + }); + dialogRef.afterClosed() + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return; } + const externalDatasetModel = new ExternalDatasetEditorModel(result.id, result.abbreviation, result.name, result.reference, result.source); + (this.formGroup.get('externalDatasets')).push(externalDatasetModel.buildForm()); + }); + } + + addService() { + const dialogRef = this.dialog.open(DatasetExternalServiceDialogEditorComponent, { + width: '500px', + restoreFocus: false, + data: {} + }); + dialogRef.afterClosed() + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return; } + const serviceModel = new ExternalServiceEditorModel(result.abbreviation, result.definition, result.id, result.label, result.reference, result.uri, result.source); + (this.formGroup.get('services')).push(serviceModel.buildForm()); + }); + } + + searchDatasetExternalDatasets(query: string, type: string): Observable { + const requestItem: RequestItem = new RequestItem(); + requestItem.criteria = new ExternalDatasetCriteria(); + requestItem.criteria.like = query; + requestItem.criteria.type = type; + return this.externalSourcesService.searchDatasetSExternalDatasetservice(requestItem); + } + + searchDatasetExternalDataRepositories(query: string, type: string): Observable { + const requestItem: RequestItem = new RequestItem(); + requestItem.criteria = new DataRepositoryCriteria(); + requestItem.criteria.like = query; + requestItem.criteria.type = type; + return this.externalSourcesService.searchDatasetRepository(requestItem); + } + + searchDatasetExternalRegistries(query: string, type: string): Observable { + const requestItem: RequestItem = new RequestItem(); + requestItem.criteria = new RegistryCriteria(); + requestItem.criteria.like = query; + requestItem.criteria.type = type; + return this.externalSourcesService.searchDatasetRegistry(requestItem); + } + + searchDatasetExternalServices(query: string, type: string): Observable { + const requestItem: RequestItem = new RequestItem(); + requestItem.criteria = new ServiceCriteria(); + requestItem.criteria.like = query; + requestItem.criteria.type = type; + return this.externalSourcesService.searchDatasetService(requestItem); + } + + searchDatasetTags(query: string, type: string): Observable { + const requestItem: RequestItem = new RequestItem(); + requestItem.criteria = new TagCriteria(); + requestItem.criteria.like = query; + requestItem.criteria.type = type; + return this.externalSourcesService.searchDatasetTags(requestItem); + } + + removeTag(tag: any) { + (this.formGroup.get('tags')).removeAt(((this.formGroup.get('tags')).value as any[]).indexOf(tag)); + } + + addTag(ev: any) { + let item: ExternalTagEditorModel; + //this.filteredTags = this.formGroup.get('tags').value; + if (typeof ev === 'string') { + item = new ExternalTagEditorModel('', ev); + } else { + item = ev; + } + if (item.name !== '' ) { + return item; + } + } + + isInternal(element: any): boolean { + if (element.get('source') == null) { + // console.log(element); + } + return element.get('source').value === 'Internal'; + } + + updateDataRepository(dataRepository: UntypedFormGroup) { + this.externalDataRepositoryService.create(dataRepository.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (result) => { + dataRepository.setValue(result); + } + ); + } + + updateExternalDataset(externalDataset: UntypedFormGroup) { + this.externalDatasetService.create(externalDataset.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (result) => { + externalDataset.setValue(result); + } + ); + } + + updateRegistry(registry: UntypedFormGroup) { + this.externalRegistryService.create(registry.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (result) => { + registry.setValue(result); + } + ); + } + + updateService(service: UntypedFormGroup) { + this.externalServiceService.create(service.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (result) => { + service.setValue(result); + } + ); + } + + filterTags(value: string): Observable { + this.filteringTagsAsync = true; + + const requestItem: RequestItem = new RequestItem(); + const criteria: TagCriteria = new TagCriteria(); + criteria.like = value; + requestItem.criteria = criteria; + return this.externalSourcesService.searchDatasetTags(requestItem); + } + + showTag(ev: any) { + if (typeof ev === 'string') { + return ev; + } else { + return ev.name; + } + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.html new file mode 100644 index 000000000..2367cc238 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.html @@ -0,0 +1,28 @@ +
+
+
+ {{'DATASET-REFERENCED-MODELS.DATA-REPOSITORY.TITLE' | translate}} +
+
+ close +
+
+
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.scss new file mode 100644 index 000000000..b1538a730 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.scss @@ -0,0 +1,3 @@ +.close-btn { + cursor: pointer; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.ts new file mode 100644 index 000000000..9c53aa709 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/data-repository/dataset-external-data-repository-dialog-editor.component.ts @@ -0,0 +1,42 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ExternalDataRepositoryService } from '@app/core/services/external-sources/data-repository/extternal-data-repository.service'; +import { ExternalDataRepositoryEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { BaseComponent } from '@common/base/base.component'; +import { FormService } from '@common/forms/form-service'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + templateUrl: 'dataset-external-data-repository-dialog-editor.component.html', + styleUrls: ['./dataset-external-data-repository-dialog-editor.component.scss'] +}) +export class DatasetExternalDataRepositoryDialogEditorComponent extends BaseComponent implements OnInit { + public formGroup: UntypedFormGroup; + + constructor( + private externalDataRepositoryService: ExternalDataRepositoryService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private formService: FormService + ) { super(); } + + ngOnInit(): void { + const datarepo = new ExternalDataRepositoryEditorModel(); + this.formGroup = datarepo.buildForm(); + } + + send(value: any) { + this.formService.touchAllFormFields(this.formGroup); + if (!this.formGroup.valid) { return; } + this.externalDataRepositoryService.create(this.formGroup.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (item) => this.dialogRef.close(item) + ); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.html new file mode 100644 index 000000000..530a33bd4 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.html @@ -0,0 +1,24 @@ +
+
+
+ {{'DATASET-REFERENCED-MODELS.EXTERNAL-DATASET.TITLE' | translate}} +
+
+ close +
+
+
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.scss new file mode 100644 index 000000000..b1538a730 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.scss @@ -0,0 +1,3 @@ +.close-btn { + cursor: pointer; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.ts new file mode 100644 index 000000000..676181d56 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/external-dataset/dataset-external-dataset-dialog-editor.component.ts @@ -0,0 +1,42 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ExternalDatasetService } from '@app/core/services/external-sources/dataset/external-dataset.service'; +import { ExternalDatasetEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { BaseComponent } from '@common/base/base.component'; +import { takeUntil } from 'rxjs/operators'; +import { FormService } from '@common/forms/form-service'; + +@Component({ + templateUrl: 'dataset-external-dataset-dialog-editor.component.html', + styleUrls: ['./dataset-external-dataset-dialog-editor.component.scss'] +}) +export class DatasetExternalDatasetDialogEditorComponent extends BaseComponent implements OnInit { + public formGroup: UntypedFormGroup; + + constructor( + private externalDatasetService: ExternalDatasetService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private formService: FormService + ) { super(); } + + ngOnInit(): void { + const externalDatasetModel = new ExternalDatasetEditorModel(); + this.formGroup = externalDatasetModel.buildForm(); + } + + send(value: any) { + this.formService.touchAllFormFields(this.formGroup); + if (!this.formGroup.valid) { return; } + this.externalDatasetService.create(this.formGroup.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (item) => this.dialogRef.close(item) + ); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.html new file mode 100644 index 000000000..111e52fc6 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.html @@ -0,0 +1,28 @@ +
+
+
+ {{'DATASET-REFERENCED-MODELS.REGISTRY.TITLE' | translate}} +
+
+ close +
+
+
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.scss new file mode 100644 index 000000000..b1538a730 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.scss @@ -0,0 +1,3 @@ +.close-btn { + cursor: pointer; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.ts new file mode 100644 index 000000000..9179bdbc7 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/registry/dataset-external-registry-dialog-editor.component.ts @@ -0,0 +1,42 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ExternalRegistryService } from '@app/core/services/external-sources/registry/external-registry.service'; +import { ExternalRegistryEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { BaseComponent } from '@common/base/base.component'; +import { FormService } from '@common/forms/form-service'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + templateUrl: 'dataset-external-registry-dialog-editor.component.html', + styleUrls: ['./dataset-external-registry-dialog-editor.component.scss'] +}) +export class DatasetExternalRegistryDialogEditorComponent extends BaseComponent implements OnInit { + public formGroup: UntypedFormGroup; + + constructor( + private externalRegistryService: ExternalRegistryService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private formService: FormService + ) { super(); } + + ngOnInit(): void { + const registryModel = new ExternalRegistryEditorModel(); + this.formGroup = registryModel.buildForm(); + } + + send(value: any) { + this.formService.touchAllFormFields(this.formGroup); + if (!this.formGroup.valid) { return; } + this.externalRegistryService.create(this.formGroup.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (item) => this.dialogRef.close(item) + ); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.html new file mode 100644 index 000000000..994c214e7 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.html @@ -0,0 +1,28 @@ +
+
+
+ {{'DATASET-REFERENCED-MODELS.SERVICES.TITLE' | translate}} +
+
+ close +
+
+
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.scss new file mode 100644 index 000000000..b1538a730 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.scss @@ -0,0 +1,3 @@ +.close-btn { + cursor: pointer; +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.ts new file mode 100644 index 000000000..5da9f1b59 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/external-references/editors/service/dataset-external-service-dialog-editor.component.ts @@ -0,0 +1,42 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ExternalServiceService } from '@app/core/services/external-sources/service/external-service.service'; +import { ExternalServiceEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { BaseComponent } from '@common/base/base.component'; +import { takeUntil } from 'rxjs/operators'; +import { FormService } from '@common/forms/form-service'; + +@Component({ + templateUrl: 'dataset-external-service-dialog-editor.component.html', + styleUrls: ['./dataset-external-service-dialog-editor.component.scss'] +}) +export class DatasetExternalServiceDialogEditorComponent extends BaseComponent implements OnInit { + public formGroup: UntypedFormGroup; + + constructor( + private externalServiceService: ExternalServiceService, + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private formService: FormService + ) { super(); } + + ngOnInit(): void { + const serviceModel = new ExternalServiceEditorModel(); + this.formGroup = serviceModel.buildForm(); + } + + send() { + this.formService.touchAllFormFields(this.formGroup); + if (!this.formGroup.valid) { return; } + this.externalServiceService.create(this.formGroup.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + (item) => this.dialogRef.close(item) + ); + } + + close() { + this.dialogRef.close(false); + } +} diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.html b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.html new file mode 100644 index 000000000..b6d159d7b --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.html @@ -0,0 +1,57 @@ +
+
+ {{'DATASET-CREATE-WIZARD.PREFILL-STEP.TITLE' | translate}} + close +
+
+ +
+
+
+
+ {{'DATASET-CREATE-WIZARD.PREFILL-STEP.HINT' | translate}} +
+
+
+ +
{{'DATASET-CREATE-WIZARD.PREFILL-STEP.OR' | translate}}
+ +
+
+
+

{{'DATASET-CREATE-WIZARD.PREFILL-STEP.PROFILE' | translate}}

+
+ + + +
+ {{profile.label}} +
+
+
+ {{prefillForm.get('profile').getError('backendError').message}} +
+
+
+
+

{{'DATASET-CREATE-WIZARD.PREFILL-STEP.PREFILLED-DATASET' | translate}}

+
+ + + + +
+ +
+
+
+ +
+
+
diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.scss b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.scss new file mode 100644 index 000000000..7a892e68b --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.scss @@ -0,0 +1,57 @@ +.template-container { + .header { + display: flex; + width: 100%; + height: 60px; + background-color: var(--secondary-color); + color: #212121; + font-size: 1.25rem; + } + + .template-title { + margin-left: 37px; + white-space: nowrap; + width: 480px; + overflow: hidden; + text-overflow: ellipsis; + } + + .close-icon { + cursor: pointer; + margin-right: 20px; + } + + .close-icon:hover { + background-color: #fefefe6e !important; + border-radius: 50%; + } + + .definition-content { + display: block; + margin: 0; + padding: 25px; + } + + .empty-btn, .prefill-btn { + background: var(--secondary-color) 0 0 no-repeat padding-box; + border: 1px solid var(--secondary-color); + border-radius: 30px; + opacity: 1; + min-width: 101px; + height: 43px; + color: #212121; + font-weight: 500; + } + + .prefill-btn:disabled { + background: #a1a1a1 0 0 no-repeat padding-box; + border: 1px solid #a1a1a1; + opacity: 0.5; + } + + .empty-btn { + background: #ffffff 0 0 no-repeat padding-box; + border: 1px solid #a1a1a1; + } +} + diff --git a/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.ts b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.ts new file mode 100644 index 000000000..08bdc21d1 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/dataset-wizard/prefill-dataset/prefill-dataset.component.ts @@ -0,0 +1,200 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from "@angular/material/dialog"; +import { DatasetProfileModel } from "@app/core/model/dataset/dataset-profile"; +import { Prefilling } from "@app/core/model/dataset/prefilling"; +import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from "@app/core/model/dmp-blueprint/dmp-blueprint"; +import { DmpBlueprintService } from "@app/core/services/dmp/dmp-blueprint.service"; +import { PrefillingService } from "@app/core/services/prefilling.service"; +import { ProgressIndicationService } from "@app/core/services/progress-indication/progress-indication-service"; +import { SingleAutoCompleteConfiguration } from "@app/library/auto-complete/single/single-auto-complete-configuration"; +import { PopupNotificationDialogComponent } from "@app/library/notification/popup/popup-notification.component"; +import { BaseComponent } from "@common/base/base.component"; +import { Guid } from "@common/types/guid"; +import { TranslateService } from "@ngx-translate/core"; +import { Observable } from "rxjs"; +import { map, takeUntil } from "rxjs/operators"; +import { nameof } from "ts-simple-nameof"; + +@Component({ + selector: 'prefill-dataset-component', + templateUrl: 'prefill-dataset.component.html', + styleUrls: ['prefill-dataset.component.scss'] +}) +export class PrefillDatasetComponent extends BaseComponent implements OnInit { + + progressIndication = false; + prefillAutoCompleteConfiguration: SingleAutoCompleteConfiguration; + isPrefilled: boolean = false; + prefillForm: UntypedFormGroup; + + constructor(public dialogRef: MatDialogRef, + private prefillingService: PrefillingService, + private dmpBlueprintService: DmpBlueprintService, + private dialog: MatDialog, + private language: TranslateService, + private progressIndicationService: ProgressIndicationService, + private fb: UntypedFormBuilder, + @Inject(MAT_DIALOG_DATA) public data: any) { + super(); + } + + ngOnInit() { + this.progressIndicationService.getProgressIndicationObservable().pipe(takeUntil(this._destroyed)).subscribe(x => { + setTimeout(() => { this.progressIndication = x; }); + }); + this.prefillForm = this.fb.group({ + type: this.fb.control(false), + profile: this.fb.control('', Validators.required), + prefill: this.fb.control(null, Validators.required) + }) + if (this.data.availableProfiles && this.data.availableProfiles.length === 1) { + this.addProfileIfUsedLessThanMax(this.data.availableProfiles[0]); + } + this.prefillAutoCompleteConfiguration = { + filterFn: this.searchDatasets.bind(this), + loadDataOnStart: false, + displayFn: (item) => (item['name'].length > 60) ? (item['name'].substr(0, 60) + "...") : item['name'], + titleFn: (item) => item['name'], + subtitleFn: (item) => item['pid'] + }; + } + + addProfileIfUsedLessThanMax(profile: DatasetProfileModel) { + const dmpSectionIndex = this.data.datasetFormGroup.get('dmpSectionIndex').value; + const blueprintId = this.data.datasetFormGroup.get('dmp').value.profile.id; + this.dmpBlueprintService.getSingle(blueprintId, this.getBlueprintDefinitionFields()) + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + const section = result.definition.sections[dmpSectionIndex]; + if (section.hasTemplates) { + const foundTemplate = section.descriptionTemplates.find(template => template.descriptionTemplateId === Guid.parse(profile.id)); + if (foundTemplate !== undefined) { + let count = 0; + if (this.data.datasetFormGroup.get('dmp').value.datasets != null) { + for (let dataset of this.data.datasetFormGroup.get('dmp').value.datasets) { + if (dataset.dmpSectionIndex === dmpSectionIndex && dataset.profile.id === foundTemplate.descriptionTemplateId) { + count++; + } + } + if (count < foundTemplate.maxMultiplicity) { + this.prefillForm.get('profile').patchValue(profile); + } + } + } + else { + this.prefillForm.get('profile').patchValue(profile); + } + } + else { + this.prefillForm.get('profile').patchValue(profile); + } + }); + } + + checkMinMax(event, profile: DatasetProfileModel) { + event.stopPropagation(); + const dmpSectionIndex = this.data.datasetFormGroup.get('dmpSectionIndex').value; + const blueprintId = this.data.datasetFormGroup.get('dmp').value.profile.id; + this.dmpBlueprintService.getSingle(blueprintId, this.getBlueprintDefinitionFields()) + .pipe(takeUntil(this._destroyed)) + .subscribe(result => { + const section = result.definition.sections[dmpSectionIndex]; + if (section.hasTemplates) { + const foundTemplate = section.descriptionTemplates.find(template => template.descriptionTemplateId === Guid.parse(profile.id)); + if (foundTemplate !== undefined) { + let count = 0; + if (this.data.datasetFormGroup.get('dmp').value.datasets != null) { + for (let dataset of this.data.datasetFormGroup.get('dmp').value.datasets) { + if (dataset.dmpSectionIndex === dmpSectionIndex && dataset.profile.id === foundTemplate.descriptionTemplateId) { + count++; + } + } + if (count === foundTemplate.maxMultiplicity) { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DATASET-EDITOR.MAX-DESCRIPTION-DIALOG.TITLE'), + message: this.language.instant('DATASET-EDITOR.MAX-DESCRIPTION-DIALOG.MESSAGE') + }, maxWidth: '30em' + }); + } + else { + this.prefillForm.get('profile').setValue(profile); + } + } + } + else { + this.prefillForm.get('profile').setValue(profile); + } + } + else { + this.prefillForm.get('profile').setValue(profile); + } + }); + } + + private getBlueprintDefinitionFields() { + return [ + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.description)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.hasTemplates)].join('.'), + + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.category)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.systemFieldType)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.placeholder)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.description)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.required)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.fields), nameof(x => x.ordinal)].join('.'), + + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.id)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.descriptionTemplateId)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.minMultiplicity)].join('.'), + [nameof(x => x.definition), nameof(x => x.sections), nameof(x => x.descriptionTemplates), nameof(x => x.maxMultiplicity)].join('.'), + ] + } + + public compareWith(object1: any, object2: any) { + return object1 && object2 && object1.id === object2.id; + } + + searchDatasets(query: string): Observable { + return this.prefillingService.getPrefillingList(query).pipe(map(prefilling => prefilling.sort((a, b) => { + if (a.name > b.name) { + return 1; + } else if (a.name < b.name) { + return -1; + } else { + return 0; + } + }))); + } + + next() { + if (this.isPrefilled) { + if (this.prefillForm.get('prefill').value.data == null) { + this.prefillingService.getPrefillingDataset(this.prefillForm.get('prefill').value.pid, this.prefillForm.get('profile').value.id, this.prefillForm.get('prefill').value.key).subscribe(wizard => { + wizard.profile = this.prefillForm.get('profile').value; + this.closeDialog(wizard); + }); + } + else { + this.prefillingService.getPrefillingDatasetUsingData(this.prefillForm.get('prefill').value.data, this.prefillForm.get('profile').value.id, this.prefillForm.get('prefill').value.key).subscribe(wizard => { + wizard.profile = this.prefillForm.get('profile').value; + this.closeDialog(wizard); + }); + } + } else { + this.closeDialog(); + } + } + + closeDialog(result = null): void { + this.dialogRef.close(result); + } +} diff --git a/dmp-frontend/src/app/ui/description/description.module.ts b/dmp-frontend/src/app/ui/description/description.module.ts new file mode 100644 index 000000000..e2dba3e3e --- /dev/null +++ b/dmp-frontend/src/app/ui/description/description.module.ts @@ -0,0 +1,78 @@ +import { NgModule } from '@angular/core'; +import { FormattingModule } from '@app/core/formatting.module'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { ExportMethodDialogModule } from '@app/library/export-method-dialog/export-method-dialog.module'; +import { RichTextEditorModule } from "@app/library/rich-text-editor/rich-text-editor.module"; +import { UrlListingModule } from '@app/library/url-listing/url-listing.module'; +// import { DescriptionEditorComponent } from '@app/ui/description/description-wizard/description-editor/description-editor.component'; +// import { DescriptionWizardComponent } from '@app/ui/description/description-wizard/description-wizard.component'; +// import { DescriptionExternalReferencesEditorComponent } from '@app/ui/description/description-wizard/external-references/description-external-references-editor.component'; +// import { DescriptionExternalDataRepositoryDialogEditorComponent } from '@app/ui/description/description-wizard/external-references/editors/data-repository/description-external-data-repository-dialog-editor.component'; +// import { DescriptionExternalDescriptionDialogEditorComponent } from '@app/ui/description/description-wizard/external-references/editors/external-description/description-external-description-dialog-editor.component'; +// import { DescriptionExternalRegistryDialogEditorComponent } from '@app/ui/description/description-wizard/external-references/editors/registry/description-external-registry-dialog-editor.component'; +// import { DescriptionExternalServiceDialogEditorComponent } from '@app/ui/description/description-wizard/external-references/editors/service/description-external-service-dialog-editor.component'; +// import { PrefillDescriptionComponent } from "@app/ui/description/description-wizard/prefill-description/prefill-description.component"; +import { DescriptionRoutingModule } from '@app/ui/description/description.routing'; +// import { DescriptionCriteriaComponent } from '@app/ui/description/listing/criteria/description-criteria.component'; +// import { DescriptionUploadDialogue } from '@app/ui/description/listing/criteria/description-upload-dialogue/description-upload-dialogue.component'; +import { DescriptionListingComponent } from '@app/ui/description/listing/description-listing.component'; +import { DescriptionListingItemComponent } from '@app/ui/description/listing/listing-item/description-listing-item.component'; +// import { DescriptionDescriptionFormModule } from '@app/ui/misc/description-description-form/description-description-form.module'; +// import { TableOfContentsModule } from '@app/ui/misc/description-description-form/tableOfContentsMaterial/table-of-contents.module'; +import { ExternalSourcesModule } from '@app/ui/misc/external-sources/external-sources.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { FormValidationErrorsDialogModule } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { AngularStickyThingsModule } from '@w11k/angular-sticky-things'; +// import { FormProgressIndicationModule } from '../misc/description-description-form/components/form-progress-indication/form-progress-indication.module'; +// import { DescriptionCopyDialogModule } from './description-wizard/description-copy-dialogue/description-copy-dialogue.module'; +// import { DescriptionCriteriaDialogComponent } from './listing/criteria/description-criteria-dialogue/description-criteria-dialog.component'; +// import { DescriptionOverviewModule } from './overview/description-overview.module'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + UrlListingModule, + FormattingModule, + ConfirmationDialogModule, + AutoCompleteModule, + ExternalSourcesModule, + ExportMethodDialogModule, + // DescriptionDescriptionFormModule, + // TableOfContentsModule, + AngularStickyThingsModule, + DescriptionRoutingModule, + FormValidationErrorsDialogModule, + // DescriptionCopyDialogModule, + // DescriptionOverviewModule, + // FormProgressIndicationModule, + RichTextEditorModule + ], + declarations: [ + DescriptionListingComponent, + // DescriptionCriteriaComponent, + // DescriptionWizardComponent, + // DescriptionEditorComponent, + // DescriptionExternalReferencesEditorComponent, + // DescriptionExternalDataRepositoryDialogEditorComponent, + // DescriptionExternalDescriptionDialogEditorComponent, + // DescriptionExternalRegistryDialogEditorComponent, + // DescriptionExternalServiceDialogEditorComponent, + // DescriptionUploadDialogue, + DescriptionListingItemComponent, + // DescriptionCriteriaDialogComponent, + // PrefillDescriptionComponent + ], + exports: [ + // DescriptionExternalReferencesEditorComponent, + // DescriptionExternalDataRepositoryDialogEditorComponent, + // DescriptionExternalDescriptionDialogEditorComponent, + // DescriptionExternalRegistryDialogEditorComponent, + // DescriptionExternalServiceDialogEditorComponent, + // DescriptionEditorComponent, + // DescriptionDescriptionFormModule + ] +}) +export class DescriptionModule { } diff --git a/dmp-frontend/src/app/ui/description/description.routing.ts b/dmp-frontend/src/app/ui/description/description.routing.ts new file mode 100644 index 000000000..9d2b4ecdb --- /dev/null +++ b/dmp-frontend/src/app/ui/description/description.routing.ts @@ -0,0 +1,129 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { CanDeactivateGuard } from '@app/library/deactivate/can-deactivate.guard'; +import { AuthGuard } from '../../core/auth-guard.service'; +// import { DescriptionWizardComponent } from './description-wizard/description-wizard.component'; +import { DescriptionListingComponent } from './listing/description-listing.component'; +// import { DescriptionOverviewComponent } from './overview/description-overview.component'; + +const routes: Routes = [ + { + path: '', + component: DescriptionListingComponent, + // canActivate: [AuthGuard], + data: { + breadcrumb: true + }, + }, + { + path: 'dmp/:dmpId', + component: DescriptionListingComponent, + canActivate: [AuthGuard], + data: { + breadcrumb: true + }, + }, + // { + // path: 'new/:dmpId/:dmpSectionIndex', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-NEW' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'edit/:id', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // public: false, + // title: 'GENERAL.TITLES.DATASET-EDIT' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'edit/:id/finalize', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // public: false, + // title: 'GENERAL.TITLES.DATASET-EDIT', + // finalize: true + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'publicEdit/:publicId', + // component: DescriptionWizardComponent, + // //canActivate: [AuthGuard], + // data: { + // public: true, + // title: 'GENERAL.TITLES.DATASET-PUBLIC-EDIT' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'new', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-NEW' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + { + path: 'dmp/:dmpId', + component: DescriptionListingComponent, + canActivate: [AuthGuard], + data: { + breadcrumb: true + }, + }, + // { + // path: 'copy/:id', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-COPY' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'profileupdate/:updateId', + // component: DescriptionWizardComponent, + // canActivate: [AuthGuard], + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-UPDATE' + // }, + // canDeactivate:[CanDeactivateGuard] + // }, + // { + // path: 'overview/:id', + // component: DescriptionOverviewComponent, + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-OVERVIEW' + // }, + // }, + // { + // path: 'publicOverview/:publicId', + // component: DescriptionOverviewComponent, + // data: { + // breadcrumb: true, + // title: 'GENERAL.TITLES.DATASET-OVERVIEW' + // }, + // } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class DescriptionRoutingModule { } diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.html b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.html new file mode 100644 index 000000000..db2bddfee --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.html @@ -0,0 +1,2 @@ +clear + diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.scss b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.scss new file mode 100644 index 000000000..6ad258085 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.scss @@ -0,0 +1,20 @@ +.clear-icon { + cursor: pointer; + color: #212121; + padding: 0.4rem; +} + +.clear-icon:hover { + background-color: #ececec !important; + border-radius: 50%; + padding: 0.4rem; +} + +:host ::ng-deep .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts new file mode 100644 index 000000000..d640a6d1f --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts @@ -0,0 +1,44 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, Inject, OnInit, ViewChild } from '@angular/core'; +import { UntypedFormGroup } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { DatasetCriteriaComponent } from '../dataset-criteria.component'; + +@Component({ + selector: 'dataset-criteria-dialog-component', + templateUrl: './dataset-criteria-dialog.component.html', + styleUrls: ['./dataset-criteria-dialog.component.scss'] +}) + +export class DatasetCriteriaDialogComponent implements OnInit { + + @ViewChild(DatasetCriteriaComponent, { static: true }) criteria: DatasetCriteriaComponent; + + constructor( + public dialogRef: MatDialogRef, + private httpClient: HttpClient, + private matomoService: MatomoService, + @Inject(MAT_DIALOG_DATA) public data: { isPublic: boolean, status: Number, criteria: DatasetCriteria, formGroup: UntypedFormGroup, updateDataFn: Function } + ) { + } + + ngOnInit() { + this.matomoService.trackPageView('Dataset Criteria'); + this.criteria.setCriteria(this.data.criteria); + } + + onNoClick(): void { + this.dialogRef.close(); + } + + onClose(): void { + this.dialogRef.close(); + } + + onFiltersChanged(event) { + this.data.updateDataFn(this.criteria); + } + +} diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.html b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.html new file mode 100644 index 000000000..6498dc3f5 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.html @@ -0,0 +1,200 @@ +
+
+
+
+
{{'CRITERIA.FILTERS'| translate}}
+
+
+
+ + + + + +
+
{{'CRITERIA.DATA-SETS.STATUS'| translate}}
+ + + {{ 'TYPES.DATASET-STATUS.ANY' | translate }} + + + {{ 'TYPES.DATASET-STATUS.DRAFT' | translate }} + + + {{ 'TYPES.DATASET-STATUS.FINALISED' | translate }} + + +
+
+ + + +
+
{{ 'FACET-SEARCH.GRANT-STATUS.TITLE' | translate }}
+ + + {{ 'FACET-SEARCH.GRANT-STATUS.OPTIONS.ANY' | translate }} + + + {{ 'FACET-SEARCH.GRANT-STATUS.OPTIONS.ACTIVE' | translate }} + + + {{ 'FACET-SEARCH.GRANT-STATUS.OPTIONS.INACTIVE' | translate }} + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.RELATED-DATASET-TEMPLATES' | translate}}
+ + {{'CRITERIA.DATA-SETS.SELECT-DATASET-TEMPLATES' | translate }} + + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.RELATED-DMP' | translate}}
+ + {{'CRITERIA.DATA-SETS.SELECT-DMP' | translate }} + + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.ALL-VERSIONS'| translate}}
+ +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.RELATED-GRANT' | translate}}
+ + {{'CRITERIA.DATA-SETS.SELECT-GRANTS' | translate }} + + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.RELATED-COLLABORATORS' | translate}}
+ + {{'CRITERIA.DATA-SETS.SELECT-COLLABORATORS' | translate }} + + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.ROLE' | translate }}
+ + + {{ 'TYPES.DATASET-ROLE.ANY' | translate }} + + + {{ 'TYPES.DATASET-ROLE.OWNER' | translate }} + + + {{ 'TYPES.DATASET-ROLE.MEMBER' | translate }} + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.ORGANIZATION' | translate }}
+ + {{'CRITERIA.DATA-SETS.SELECT-ORGANIZATIONS' | translate}} + + + +
+
+ + + +
+
{{'CRITERIA.DATA-SETS.TAGS' | translate }}
+ + {{'CRITERIA.DATA-SETS.SELECT-TAGS' | translate}} + + + +
+ + + + + +
+
+
+ + diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.scss b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.scss new file mode 100644 index 000000000..a8e92de7c --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.scss @@ -0,0 +1,56 @@ +.search ::ng-deep.mat-form-field-infix { + margin-left: 1em; +} + +.category-title { + color: black; + // color: #089dbb; + margin-top: 8px; + margin-bottom: 12px; + text-transform: none; + font-size: 1rem; + font-weight: 400; +} + +.import { + margin: 10px; + padding: 0px; +} + +.filters { + // border: 1px solid #e4e4e4; + // border-radius: 5px; +} + +.criteria-title { + color: black; + font-weight: 500; + text-transform: none; + font-size: 1.25rem; + margin-bottom: 1.5em; + opacity: 0.8; +} + +.filters-title { + width: 93px; + // color: #089dbb; + color: var(--primary-color-2); + background-color: white; + padding: 0px 20px; + margin-top: -10px; + margin-left: 20px; + text-transform: uppercase; +} + +.style-icon { + color: #adadad; +} + +::ng-deep .search-form .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.ts b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.ts new file mode 100644 index 000000000..a05881fc8 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-criteria.component.ts @@ -0,0 +1,339 @@ + +import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; +import { DataTableData } from '@app/core/model/data-table/data-table-data'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; +import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; +import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; +import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; +import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; +import { GrantCriteria } from '@app/core/query/grant/grant-criteria'; +import { OrganisationCriteria } from '@app/core/query/organisation/organisation-criteria'; +import { RequestItem } from '@app/core/query/request-item'; +import { TagCriteria } from '@app/core/query/tag/tag-criteria'; +import { UserCriteria } from '@app/core/query/user/user-criteria'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DatasetService } from '@app/core/services/dataset/dataset.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; +import { GrantService } from '@app/core/services/grant/grant.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { OrganisationService } from '@app/core/services/organisation/organisation.service'; +import { UserServiceOld } from '@app/core/services/user/user.service-old'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { DatasetUploadDialogue } from '@app/ui/dataset/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component'; +import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { ExploreDmpCriteriaModel } from '@app/core/query/explore-dmp/explore-dmp-criteria'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; + +@Component({ + selector: 'app-dataset-criteria-component', + templateUrl: './dataset-criteria.component.html', + styleUrls: ['./dataset-criteria.component.scss'] +}) +export class DatasetCriteriaComponent extends BaseCriteriaComponent implements OnInit { + + @Input() dmpSearchEnabled; + @Input() status; + @Input() isPublic: boolean; + @Input() criteriaFormGroup: UntypedFormGroup; + @Output() filtersChanged: EventEmitter = new EventEmitter(); + public criteria: any; + public filteringTagsAsync = false; + public filteredTags: ExternalSourceItemModel[]; + statuses = DatasetStatus; + + options: UntypedFormGroup; + + public formGroup = new UntypedFormBuilder().group({ + like: new UntypedFormControl(), + groupIds: new UntypedFormControl(), + grants: new UntypedFormControl(), + status: new UntypedFormControl(), + role: new UntypedFormControl(), + organisations: new UntypedFormControl(), + collaborators: new UntypedFormControl(), + datasetTemplates: new UntypedFormControl(), + tags: new UntypedFormControl(), + allVersions: new UntypedFormControl(), + grantStatus: new UntypedFormControl() + }); + + tagsAutoCompleteConfiguration = { + filterFn: this.filterTags.bind(this), + initialItems: (excludedItems: any[]) => this.filterTags('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['name'], + titleFn: (item) => item['name'] + }; + + datasetTemplateAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterDatasetTemplate.bind(this), + initialItems: (excludedItems: any[]) => this.filterDatasetTemplate('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'], + subtitleFn: (item) => item['description'] + }; + + dmpAutoCompleteConfiguration = { + filterFn: (x, excluded) => this.filterDmps(x).pipe(map(x => x.data)), + initialItems: (extraData) => this.filterDmps('').pipe(map(x => x.data)), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'] + }; + + collaboratorsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterCollaborators.bind(this), + initialItems: (excludedItems: any[]) => this.filterCollaborators('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['name'], + titleFn: (item) => item['name'] + }; + + grantAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterGrant.bind(this), + initialItems: (excludedItems: any[]) => this.filterGrant('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'] + }; + + organisationAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = { + filterFn: this.filterOrganisations.bind(this), + initialItems: (excludedItems: any[]) => this.filterOrganisations('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))), + displayFn: (item) => item['name'], + titleFn: (item) => item['name'] + } + + constructor( + private externalSourcesService: ExternalSourcesService, + public enumUtils: EnumUtils, + public dmpService: DmpService, + public datasetWizardService: DatasetWizardService, + private dialog: MatDialog, + private snackBar: MatSnackBar, + private uiNotificationService: UiNotificationService, + private router: Router, + private language: TranslateService, + public grantService: GrantService, + private organisationService: OrganisationService, + private userService: UserServiceOld, + private datasetService: DatasetService, + fb: UntypedFormBuilder, + private authService: AuthService + + ) { + super(new ValidationErrorModel()); + // this.options = fb.group({ + // status: new FormControl(), + // floatLabel: 'auto', + // }); + } + + ngOnInit() { + super.ngOnInit(); + + // This if is just for passing label on chips of dialog + if (this.formGroup && this.criteriaFormGroup) { + this.formGroup.get('datasetTemplates').setValue(this.criteriaFormGroup.get('datasetTemplates').value); + this.formGroup.get('groupIds').setValue(this.criteriaFormGroup.get('groupIds').value); + this.formGroup.get('grants').setValue(this.criteriaFormGroup.get('grants').value); + this.formGroup.get('collaborators').setValue(this.criteriaFormGroup.get('collaborators').value); + this.formGroup.get('organisations').setValue(this.criteriaFormGroup.get('organisations').value); + this.formGroup.get('tags').setValue(this.criteriaFormGroup.get('tags').value); + + } + + this.formGroup.get('like').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('groupIds').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('grants').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('status').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('role').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('organisations').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('collaborators').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('datasetTemplates').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('allVersions').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('tags').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + this.formGroup.get('grantStatus').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => this.controlModified()); + // if (this.criteria == null) { this.criteria = {}; } + // this.formGroup.patchValue({'status': this.status !== undefined ? this.status : 'null'}); + } + + setCriteria(criteria: DatasetCriteria): void { + this.formGroup.get('like').patchValue(criteria.like); + this.formGroup.get('groupIds').patchValue(criteria.groupIds); + this.formGroup.get('grants').patchValue(criteria.grants); + this.formGroup.get('status').patchValue(criteria.status); + this.formGroup.get('role').patchValue(criteria.role); + this.formGroup.get('collaborators').patchValue(criteria.collaborators); + this.formGroup.get('datasetTemplates').patchValue(criteria.datasetTemplates); + this.formGroup.get('allVersions').patchValue(criteria.allVersions); + this.formGroup.get('tags').patchValue(criteria.tags); + this.formGroup.get('grantStatus').patchValue(criteria.grantStatus); + // this.criteria = criteria; + } + + controlModified(): void { + this.clearErrorModel(); + this.filtersChanged.emit(); + if (this.refreshCallback != null && + (this.formGroup.get('like').value == null || this.formGroup.get('like').value.length === 0 || this.formGroup.get('like').value.length > 2) + ) { + setTimeout(() => this.refreshCallback(true)); + } + // if (this.refreshCallback != null && + // (this.criteria.like == null || this.criteria.like.length === 0 || this.criteria.like.length > 2) + // ) { + // this.refreshCallback(); + // } + } + + filterTags(value: string): Observable { + this.filteredTags = undefined; + this.filteringTagsAsync = true; + + const requestItem: RequestItem = new RequestItem(); + const criteria: TagCriteria = new TagCriteria(); + criteria.like = value; + requestItem.criteria = criteria; + return this.externalSourcesService.searchDatasetTags(requestItem); + } + + filterDatasetTemplate(query: string): Observable { + const fields: Array = new Array(); + fields.push('asc'); + const datasetTemplateRequestItem: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + datasetTemplateRequestItem.criteria = new DatasetProfileCriteria(); + datasetTemplateRequestItem.criteria.like = query; + if (this.isPublic) { + return this.datasetService.getDatasetProfiles(datasetTemplateRequestItem); + } else { + return this.datasetService.getDatasetProfilesUsedPaged(datasetTemplateRequestItem).pipe(map(x => x.data)); + } + } + + filterDmps(value: string): Observable> { + const fields: Array = new Array(); + fields.push('asc'); + + // if (this.isPublic) { + // const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + // dmpDataTableRequest.criteria = new ExploreDmpCriteriaModel(); + // dmpDataTableRequest.criteria.like = value; + // return this.dmpService.getPublicPaged(dmpDataTableRequest, "autocomplete"); + // } else { + const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + dmpDataTableRequest.criteria = new DmpCriteria(); + dmpDataTableRequest.criteria.like = value; + if (this.isPublic) { + dmpDataTableRequest.criteria.isPublic = true; + dmpDataTableRequest.criteria.onlyPublic = true; + } + return this.dmpService.getPaged(dmpDataTableRequest, "autocomplete"); + // } + } + + filterGrant(query: string) { + const fields: Array = new Array(); + fields.push('asc'); + const grantRequestItem: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + grantRequestItem.criteria = new GrantCriteria(); + grantRequestItem.criteria.like = query; + if (this.isPublic) { + return this.grantService.getPublicPaged(grantRequestItem).pipe(map(x => x.data)); + } else { + return this.grantService.getPaged(grantRequestItem, "autocomplete").pipe(map(x => x.data)); + } + } + + filterOrganisations(value: string) { + const fields: Array = new Array(); + fields.push('asc'); + const dataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + dataTableRequest.criteria = new OrganisationCriteria(); + dataTableRequest.criteria.labelLike = value; + if (this.isPublic) { + return this.organisationService.searchPublicOrganisations(dataTableRequest).pipe(map(x => x.data)); + } else { + return this.organisationService.searchInternalOrganisations(dataTableRequest).pipe(map(x => x.data)); + } + + } + + filterCollaborators(query: string) { + const fields: Array = new Array(); + fields.push('asc'); + const collaboratorsRequestItem: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + collaboratorsRequestItem.criteria = new UserCriteria(); + collaboratorsRequestItem.criteria.collaboratorLike = query; + return this.userService.getCollaboratorsPaged(collaboratorsRequestItem).pipe(map(x => x.data)); + } + + fileImport(event) { + const dialogRef = this.dialog.open(DatasetUploadDialogue, { + data: { + fileList: FileList, + success: Boolean, + datasetTitle: String, + dmpId: String, + datasetProfileId: String, + dmpSearchEnabled: this.dmpSearchEnabled, + criteria: this.criteria + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result && result.success) { + this.datasetWizardService.uploadXml(result.fileList, result.datasetTitle, result.dmpId, result.datasetProfileId) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }) + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-UPLOAD.SNACK-BAR.SUCCESSFUL-CREATION'), SnackBarNotificationLevel.Success); + this.router.navigate(['/plans']); + } + + onCallbackError(error: any) { + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Success); + } + + isAuthenticated(): boolean { + return this.authService.currentAccountIsAuthenticated(); + } +} diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.html b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.html new file mode 100644 index 000000000..f631c3a39 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.html @@ -0,0 +1,42 @@ +
+
+
+

{{'DATASET-UPLOAD.TITLE' | translate}}

+
+
+ + +
+
+
+ + + + + + + + + + + + + {{datasetProfile.label}} + + + + +
+ +
+
+
+ +
+
+
diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.scss b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.scss new file mode 100644 index 000000000..c066ed1b5 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.scss @@ -0,0 +1,21 @@ +.confirmation-dialog { + .confirmation-message { + padding-bottom: 20px; + } + + .hidden { + display: none; + } + + .uploadButton { + float: right; + } + + .col-md-6 { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + margin: -4px; + } +} diff --git a/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.ts b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.ts new file mode 100644 index 000000000..307907fcd --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/criteria/dataset-upload-dialogue/dataset-upload-dialogue.component.ts @@ -0,0 +1,157 @@ + +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { DataTableData } from '@app/core/model/data-table/data-table-data'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; +import { DmpModel } from '@app/core/model/dmp/dmp'; +import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; +import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; +import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; +import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; +import { RequestItem } from '@app/core/query/request-item'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component'; +import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +import { Observable } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'dataset-upload-dialogue', + templateUrl: './dataset-upload-dialogue.component.html', + styleUrls: ['./dataset-upload-dialogue.component.scss'] +}) +export class DatasetUploadDialogue extends BaseCriteriaComponent implements OnInit { + + public dialogueCriteria: any; + datasetTitle: string; + dmp: DmpModel; + datasetProfile: DatasetProfileModel; + availableProfiles: DatasetProfileModel[] = []; + + dmpAutoCompleteConfiguration = { + filterFn: (x, excluded) => this.filterDmps(x).pipe(map(x => x.data)), + initialItems: (extraData) => this.filterDmps('').pipe(map(x => x.data)), + displayFn: (item) => item['label'], + titleFn: (item) => item['label'] + }; + + constructor( + public dialogRef: MatDialogRef, + public dmpService: DmpService, + private datasetWizardService: DatasetWizardService, + @Inject(MAT_DIALOG_DATA) public data: any, + ) { super(new ValidationErrorModel()); } + + ngOnInit() { + super.ngOnInit(); + if (this.dialogueCriteria == null) { this.dialogueCriteria = {}; } + if (!this.data.dmpSearchEnabled) { + this.dialogueCriteria = this.data.criteria; + this.dmp = this.dialogueCriteria.dmpIds[0]; + this.loadDatasetProfiles(); + } + } + + cancel() { + this.data.success = false; + this.dialogRef.close(this.data) + } + + confirm() { + this.data.success = true; + this.data.datasetTitle = this.datasetTitle; + this.data.dmpId = this.dmp.id; + this.data.datasetProfileId = this.datasetProfile.id; + this.dialogRef.close(this.data); + } + + uploadFile(event) { + const fileList: FileList = event.target.files + this.data.fileList = fileList; + if (this.data.fileList.length > 0) { + this.datasetTitle = fileList.item(0).name; + } + } + + filterDmps(value: string): Observable> { + const fields: Array = new Array(); + fields.push('asc'); + const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, null, { fields: fields }); + dmpDataTableRequest.criteria = new DmpCriteria(); + dmpDataTableRequest.criteria.like = value; + return this.dmpService.getPaged(dmpDataTableRequest, "autocomplete"); + } + + controlModified(): void { + this.loadDatasetProfiles(); + if (!this.dmp) { + this.availableProfiles = []; + } + this.clearErrorModel(); + if (this.refreshCallback != null && + (this.dialogueCriteria.like == null || this.dialogueCriteria.like.length === 0 || this.dialogueCriteria.like.length > 2) + ) { + this.refreshCallback(); + } + } + + loadDatasetProfiles() { + const datasetProfileRequestItem: RequestItem = new RequestItem(); + datasetProfileRequestItem.criteria = new DatasetProfileCriteria(); + if (this.dmp) { + datasetProfileRequestItem.criteria.id = this.dmp.id; + } + if (datasetProfileRequestItem.criteria.id) { + this.datasetWizardService.getAvailableProfiles(datasetProfileRequestItem) + .pipe(takeUntil(this._destroyed)) + .subscribe(items => { + this.availableProfiles = items; + if (this.availableProfiles.length === 1) { + this.datasetProfile = this.availableProfiles[0]; + } + }); + } + } + + setCriteriaDialogue(criteria: DatasetCriteria): void { + this.dialogueCriteria = criteria; + } + + disableButton() { + if (!(this.data.fileList.length > 0) || !this.dmp) { + return true; + } + else { + return false; + } + } + + disableDatasetName() { + if (!(this.data.fileList.length > 0)) { + return true; + } + else { + return false; + } + } + + disableDmpSearch() { + if (!(this.data.fileList.length > 0) || !this.data.dmpSearchEnabled) { + return true; + } + else { + return false; + } + } + + disableDatasetProfile() { + if (!this.dmp || (!this.data.dmpSearchEnabled && !(this.data.fileList.length > 0)) || (!this.data.dmpSearchEnabled && this.availableProfiles.length === 1)) { + return true; + } + else { + return false; + } + } +} diff --git a/dmp-frontend/src/app/ui/description/listing/description-listing.component.html b/dmp-frontend/src/app/ui/description/listing/description-listing.component.html new file mode 100644 index 000000000..6c094d9bc --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/description-listing.component.html @@ -0,0 +1,78 @@ +
+
+
+
+ + +
+

{{'DATASET-LISTING.TEXT-INFO' | translate}} {{'DATASET-LISTING.LINK-PUBLIC-DATASETS' | translate}} {{'DATASET-LISTING.TEXT-INFO-REST' | translate}}

+

{{'DATASET-LISTING.TEXT-INFO-PAR' | translate}} +

+ + +
+
+
+

{{(isPublic ? 'GENERAL.TITLES.EXPLORE' : 'GENERAL.TITLES.DESCRIPTIONS') | translate}}

+
+
+ +
+
+
+ +
+
+
+
+
+
+ + {{'DMP-LISTING.SORT-BY' | translate}}: + + + {{enumUtils.toRecentActivityOrderString(order.MODIFIED)}} + {{enumUtils.toRecentActivityOrderString(order.DATASETPUBLISHED)}} + {{enumUtils.toRecentActivityOrderString(order.LABEL)}} + {{enumUtils.toRecentActivityOrderString(order.STATUS)}} + + + + +
+ +
+ {{ 'GENERAL.ACTIONS.TAKE-A-TOUR'| translate }} +
+ + + + search + + {{formGroup.get('like').getError('backendError').message}} + + +
+
+
+
+
+ +
+
+ +
+
+
+ {{'DATASET-LISTING.EMPTY-LIST' | translate}} +
+
+
+
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/description/listing/description-listing.component.scss b/dmp-frontend/src/app/ui/description/listing/description-listing.component.scss new file mode 100644 index 000000000..5042fe772 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/description-listing.component.scss @@ -0,0 +1,265 @@ +@import "node_modules/bootstrap/scss/functions"; +@import "node_modules/bootstrap/scss/variables"; +@import "node_modules/bootstrap/scss/mixins/_breakpoints"; + +@include media-breakpoint-down(sm) { + .lightblue-btn { + font-size: 12px; + } + ::ng-deep .mat-paginator-container { + height: auto !important; + } +} + +.header-image { + background: url("/assets/images/new-dashboard-bg.png") no-repeat; + background-size: cover; + margin-top: 70px; + min-height: 15em; + position: relative; +} + +.header-title { + text-align: left; + font-size: 1.25rem; + font-weight: 300; + color: #212121; + padding: 0px; +} + +.header-text-container { + background: rgba(255, 255, 255, 0.7); + position: absolute; + bottom: 0px; + padding-left: 5em; + padding-right: 10em; + padding-top: 2em; + padding-bottom: 2em; +} + +.explore-dmp-content { + padding: 30px 15px; +} + +.main-content { + background-color: #f5f5f5; + padding-top: 4.68rem; + padding-bottom: 3rem; + padding-left: 3rem; + padding-right: 3rem; + margin: 0; + display: flex; + flex-grow: 1; +} + +.card { + background: #ffffff 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #00000029; + border-radius: 4px; + // width: 987px; + margin-bottom: 3.75rem; + /* height: 407px; */ + opacity: 1; +} + +.card-title { + text-align: left; + font: Bold 20px/30px Roboto; + letter-spacing: 0px; + color: #212121; + padding-left: 40px; + /* padding-top: 40px; */ + padding-right: 55px; + opacity: 1; +} + +.card-content { + text-align: left; + font-weight: 300; + letter-spacing: 0px; + color: #212121; + padding-left: 40px; + padding-top: 36px; +// padding-bottom: 36px; + padding-right: 55px; + opacity: 1; +} + +.clear-icon { + cursor: pointer; + color: #212121; + padding: 0.4rem; +} + +.clear-icon:hover { + background-color: #ececec !important; + border-radius: 50%; + padding: 0.4rem; +} + +.normal-btn { + min-width: 162px; + max-width: 256px; + height: 40px; + cursor: pointer; + background: var(--primary-color) 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1e202029; + border-radius: 30px; + border: none; + color: #ffffff; + opacity: 1; + line-height: 1; + font-size: 0.87rem; + padding: 0.62rem 1.87rem; + margin-left: 40px; +} + +.yellow-btn { + min-width: 162px; + max-width: 256px; + height: 40px; + cursor: pointer; + background: var(--secondary-color) 0% 0% no-repeat padding-box; + border-radius: 30px; + opacity: 1; + border: none; + color: #000000; + opacity: 1; + line-height: 1; + font-size: 0.87rem; + padding: 0.62rem 1.87rem; + font-weight: 700; +} + +.info-text { + text-align: left; + font-weight: 300; + font-size: 1rem; + letter-spacing: 0px; + color: #212121; +} + +.filter-btn { + position: fixed; + right: 0px; + z-index: 100; + width: 37px; +} + +.filter-btn button { + color: white; + background-color: var(--primary-color-2); + width: 37px; + height: 45px; +} + +.center-content { + width: 100%; + min-width: 10rem; + margin: auto; + padding: 0 15px; + text-align: right; + font-size: 0.875rem; + font-weight: 600; + letter-spacing: 0.02rem; + color: #2d72d6; + cursor: pointer; +} + +.search-form { + // font-size: 12px; + text-align: left; + width: 20rem; +} + +.search-form mat-icon { + color: var(--primary-color); +} + +.empty-list { + text-align: center; + font-weight: 300; + font-size: 1.25rem; + letter-spacing: 0px; + color: #212121; + opacity: 0.6; +} + +.pointer:hover { + color: var(--primary-color-3); +} + +::ng-deep .search-form .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +::ng-deep .sort-form .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} + +:host ::ng-deep .mat-paginator-container { + flex-direction: row-reverse !important; + justify-content: space-between !important; + background-color: #f6f6f6; + height: 30px; + min-height: 30px !important; +} + +:host ::ng-deep .mat-paginator-page-size { + height: 43px; +} + +:host ::ng-deep .mat-icon-button { + height: 30px !important; + font-size: 12px !important; +} + +:host ::ng-deep .mat-paginator-range-label { + margin: 15px 32px 0 24px !important; +} + +:host ::ng-deep .mat-paginator-range-actions { + width: auto !important; + min-width: 55% !important; + min-height: 43px !important; + justify-content: space-between; +} + +:host ::ng-deep .mat-paginator-navigation-previous { + margin-left: auto !important; +} + +::ng-deep .mat-ripple-element { + background-color: #2e74b649 !important; +} + +::ng-deep .mat-radio-container { + border-radius: 1em; + background: white; +} + +::ng-deep .mat-radio-button .mat-radio-outer-circle { + border: 1px solid #aaaaaa; +} + +::ng-deep .mat-radio-button.mat-accent.mat-radio-checked .mat-radio-outer-circle { + border-color: #777777; + // border-color: var(--primary-color-3); +} + +::ng-deep .mat-radio-button.mat-accent .mat-radio-inner-circle { + // color: var(--primary-color-3); + // background-color: var(--primary-color-3); + color: #777777; + background-color: #777777; +} + +.mat-radio-button.mat-accent .mat-radio-ripple .mat-ripple-element { + background-color: #2e74b649; +} diff --git a/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts new file mode 100644 index 000000000..a5f59dbbc --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/description-listing.component.ts @@ -0,0 +1,321 @@ + +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { DescriptionStatus } from '@app/core/common/enum/description-status'; +import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; +import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; +import { Description } from '@app/core/model/description/description'; +import { DescriptionLookup } from '@app/core/query/description.lookup'; +import { DmpLookup } from '@app/core/query/dmp.lookup'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { DescriptionService } from '@app/core/services/description/description.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants'; +import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service'; +import { StartNewDmpDialogComponent } from '@app/ui/dmp/start-new-dmp-dialogue/start-new-dmp-dialog.component'; +// import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { BaseComponent } from '@common/base/base.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable, of as observableOf } from 'rxjs'; +import { debounceTime, takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + selector: 'app-description-listing-component', + templateUrl: 'description-listing.component.html', + styleUrls: ['./description-listing.component.scss'] +}) +export class DescriptionListingComponent extends BaseComponent implements OnInit {//IBreadCrumbComponent + + @ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + // @ViewChild(DescriptionCriteriaComponent, { static: true }) criteria: DescriptionCriteriaComponent; + + // breadCrumbs: Observable; + + titlePrefix: String; + dmpId: string; + status: Number; + totalCount: number; + dmpSearchEnabled = true; + listingItems: Description[] = []; + hasListingItems = null; + + isPublic: boolean = false; + public isVisible = true + + pageSize: number = 5; + lookup: DescriptionLookup = new DescriptionLookup(); + criteriaFormGroup: UntypedFormGroup; + public formGroup = new UntypedFormBuilder().group({ + like: new UntypedFormControl(), + order: new UntypedFormControl() + }); + + scrollbar: boolean; + order = RecentActivityOrder; + dmpText: string; + descriptionText: string; + + constructor( + private descriptionService: DescriptionService, + private router: Router, + private route: ActivatedRoute, + public dialog: MatDialog, + private dmpService: DmpService, + private language: TranslateService, + private authService: AuthService, + public enumUtils: EnumUtils, + private authentication: AuthService, + private guidedTourService: GuidedTourService, + private httpClient: HttpClient, + private matomoService: MatomoService + ) { + super(); + } + + ngOnInit() { + this.matomoService.trackPageView('Descriptions'); + this.isPublic = this.router.url === '/explore'; + if (this.isPublic) { + this.formGroup.get('order').setValue(this.order.DATASETPUBLISHED); + } else { + this.formGroup.get('order').setValue(this.order.MODIFIED); + } + if (!this.isPublic && !this.authService.currentAccountIsAuthenticated()) { + this.router.navigateByUrl("/explore"); + } + this.route.params + .pipe(takeUntil(this._destroyed)) + .subscribe(async (params: Params) => { + const queryParams = this.route.snapshot.queryParams; + this.dmpId = queryParams['dmpId']; + this.status = queryParams['status']; + + this.lookup.page = { size: this.pageSize, offset: 0 }; + this.lookup.order = { items: ['-' + nameof(x => x.updatedAt)] }; + if (this.dmpId != null && Guid.isGuid(this.dmpId)) { + this.dmpSearchEnabled = false; + //const dmp = await this.dmpService.getSingle(this.dmpId).toPromise(); + this.lookup.dmpSubQuery = new DmpLookup(); + this.lookup.dmpSubQuery.ids = [Guid.parse(this.dmpId)]; + this.refresh(this.lookup); + if (params['dmpLabel'] !== undefined) { + this.titlePrefix = 'for ' + params['dmpLabel']; + } + } + + if (this.status != null && this.status == DescriptionStatus.Draft) { //TODO: chack if actually used + this.lookup.statuses = [DescriptionStatus.Draft] + } + this.refresh(this.lookup); + }); + + this.formGroup.get('like').valueChanges + .pipe(takeUntil(this._destroyed), debounceTime(500)) + .subscribe(x => this.controlModified()); + this.formGroup.get('order').valueChanges + .pipe(takeUntil(this._destroyed)) + .subscribe(x => { + const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '' : "-") + this.formGroup.get('order').value]; + this.lookup.order = { items: fields }; + this.lookup.page = { size: this.pageSize, offset: 0 }; + this.refresh(this.lookup); + }); + } + + ngAfterContentChecked(): void { + this.scrollbar = this.hasScrollbar(); + } + + public dashboardTour: GuidedTour = { + tourId: 'dmp-description-tour', + useOrb: true, + steps: [ + { + selector: '.dmp-tour', + content: 'Step 1', + orientation: Orientation.Right, + isStepUnique: false + }, + { + selector: '.description-tour', + content: 'Step 2', + orientation: Orientation.Right, + isStepUnique: false + } + ] + }; + + public isAuthenticated(): boolean { + return this.authService.currentAccountIsAuthenticated(); + } + + controlModified(): void { + + this.lookup.like = this.formGroup.get("like").value; + this.lookup.page = { size: this.pageSize, offset: 0 }; + this.refresh(this.lookup); + } + + refresh(lookup: DescriptionLookup) { + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label), + ] + }; + this.descriptionService.query(lookup).pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return []; } + // if (this._paginator.pageIndex === 0) { this.totalCount = result.totalCount; } + this.totalCount = result.count; + if (lookup?.page?.offset === 0) this.listingItems = []; + this.listingItems = result.items; + this.hasListingItems = true; + }); + } + + openFiltersDialog(): void { + //TODO: Add filters dialog + + // const dialogRef = this.dialog.open(DescriptionCriteriaDialogComponent, { + // width: '456px', + // height: '100%', + // id: 'filters', + // restoreFocus: false, + // data: { + // isPublic: this.isPublic, + // status: this.status, + // criteria: this.criteria, + // formGroup: this.criteriaFormGroup, + // // criteria: this.grantId ? this.criteria : this.getDefaultCriteria(), + // updateDataFn: this.updateDataFn.bind(this) + // }, + // position: { right: '0px;' }, + // panelClass: 'dialog-side-panel' + // }); + + // dialogRef.afterClosed().subscribe(result => { + // }); + } + + // updateDataFn(criteria: DescriptionCriteriaComponent): void { + // this.criteriaFormGroup = criteria.formGroup; + // this.toDescriptionCriteria(criteria); + // this.refresh(); + // } + + // toDescriptionCriteria(criteria: DescriptionCriteriaComponent) { + // let formGroup = criteria.formGroup; + // this.criteria = { + // like: formGroup.get("like").value, + // status: formGroup.get("status").value, + // allVersions: formGroup.get("allVersions").value, + // role: formGroup.get("role").value + // } + // if (formGroup.get("tags") && formGroup.get("tags").value) { + // this.criteria.tags = formGroup.get("tags").value.map(x => (x)); + // } + // if (formGroup.get("collaborators") && formGroup.get("collaborators").value) { + // this.criteria.collaborators = formGroup.get("collaborators").value.map(x => x.id); + // } + // if (formGroup.get("dmpIds") && formGroup.get("dmpIds").value) { + // this.criteria.dmpIds = formGroup.get("dmpIds").value.map(x => x.id); + // } + // if (formGroup.get("groupIds") && formGroup.get("groupIds").value) { + // this.criteria.groupIds = formGroup.get("groupIds").value.map(x => x.groupId); + // } + // if (formGroup.get("grants") && formGroup.get("grants").value) { + // this.criteria.grants = formGroup.get("grants").value.map(x => x.id); + // } + // if (formGroup.get("organisations") && formGroup.get("organisations").value) { + // this.criteria.organisations = formGroup.get("organisations").value.map(x => x.id); + // } + // if (formGroup.get("descriptionTemplates") && formGroup.get("descriptionTemplates").value) { + // this.criteria.descriptionTemplates = formGroup.get("descriptionTemplates").value.map(x => x.id) + // } + // if (formGroup.get("grantStatus") && formGroup.get("grantStatus").value) { + // this.criteria.grantStatus = formGroup.get("grantStatus").value; + // } + // this.criteria.isPublic = this.isPublic; + // } + + + hasScrollbar(): boolean { + return document.getElementById("main-page").scrollHeight > document.documentElement.clientHeight + } + + public restartTour(): void { + this.setDashboardTourDmpText(); + this.setDashboardTourDescriptionText(); + this.guidedTourService.startTour(this.dashboardTour); + } + + public setDashboardTourDmpText(): void { + this.dmpText = this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' + + this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' + + this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' + + this.language.instant('DMP-LISTING.GET-IDEA'); + this.dashboardTour.steps[0].title = this.dmpText; + } + + public setDashboardTourDescriptionText(): void { + this.descriptionText = this.language.instant('DATASET-LISTING.TEXT-INFO') + + this.language.instant('DATASET-LISTING.LINK-PUBLIC-DATASETS') + ' ' + + this.language.instant('DATASET-LISTING.TEXT-INFO-REST') + '\n\n' + + this.language.instant('DATASET-LISTING.TEXT-INFO-PAR'); + this.dashboardTour.steps[1].title = this.descriptionText; + } + + openNewDmpDialog() { + if (this.dialog.openDialogs.length > 0) { + this.dialog.closeAll(); + } + else { + const dialogRef = this.dialog.open(StartNewDmpDialogComponent, { + disableClose: false, + data: { + isDialog: true + } + }); + } + } + + addNewDescription() { + //TODO: Add new description dialog + + // const dialogRef = this.dialog.open(StartNewDescriptionDialogComponent, { + // disableClose: false, + // restoreFocus: false, + // data: { + // startNewDmp: false, + // formGroup: new DescriptionWizardEditorModel().buildForm() + // } + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // if (result.startNewDmp) { + // this.openNewDmpDialog(); + // } else { + // this.router.navigate(['/plans', 'edit', result.formGroup.get('dmp').value.id]); + // } + // } + // }); + } + + hasLikeCriteria(): boolean { + return this.lookup.like !== undefined && this.lookup.like !== null; + } + +} diff --git a/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html new file mode 100644 index 000000000..5a4f347ff --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.html @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.scss b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.scss new file mode 100644 index 000000000..00f861bd0 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.scss @@ -0,0 +1,261 @@ +.gray-container { + letter-spacing: 5px; + color: #aaaaaa; +} + +.container-header { + display: flex; + align-items: baseline; + margin-top: 0px; + text-transform: uppercase; +} + +.container-header p { + letter-spacing: 5px; + color: #aaaaaa; + padding-top: 10px; + margin-bottom: 0px; +} + +.container-header :hover { + color: var(--primary-color-3); +} + +h4 { + display: inline; + padding-left: 1em; + color: #333333; +} + +h4 > span { + text-transform: uppercase; +} + +.title h4 { + padding-left: 30px; + line-height: 2em; +} + +.about-item { + display: flex; + flex-wrap: wrap; +} + +.links :hover { + color: var(--primary-color-3); +} + +.about-item .length { + color: var(--primary-color-3); +} + +.about-item .title { + margin: 2px 10px; + // text-transform: uppercase; +} + +.about-item p { + margin-left: auto; + margin-bottom: 0px; + padding-top: 7px; + color: #aaaaaa; +} + +p { + color: #333333; +} + +.storage :hover { + color: var(--primary-color-3); +} + +.draft-bookmark { + color: #e7e6e6; +} + +.finalized-bookmark { + color: #08bd63; +} + +.dmp-card, +.description-card { + min-width: 712px; + /* min-height: 308px; */ + background: #ffffff 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #0000001a; + border-radius: 4px; + opacity: 1; + margin-top: 2.43rem; + margin-bottom: 1rem; +} + +.remove-border-bottom ::ng-deep .mat-tab-header { + border-bottom: none; +} + +input[type="text"] { + background: #fafafa 0% 0% no-repeat padding-box; + border: 1px solid #d1d1d1; + border-radius: 4px; + opacity: 1; + width: 347px; + height: 56px; + font-family: Arial, FontAwesome; + padding-left: 15px; +} + +.edited-date { + text-align: left; + font-weight: 300; + font-family: "Roboto", sans-serif; + line-height: 2.4; + letter-spacing: 0px; + color: #212121; + opacity: 0.6; +} + +.dmp-label { + background: var(--primary-color) 0% 0% no-repeat padding-box; + border-radius: 4px 0px; + opacity: 1; + min-width: 67px; + height: 37px; + color: #ffffff; + line-height: 2.4; +} + +.description-label { + width: auto; + height: 37px; + background: var(--secondary-color) 0% 0% no-repeat padding-box; + border-radius: 4px 0px; + text-align: left; + line-height: 2.8; + font-size: 0.875rem; + font-weight: 400; + letter-spacing: 0px; + color: #212121; +} + +.dmp-title, +.description-title { + text-align: left; + font-weight: 600; + font-family: "Roboto", sans-serif; + font-size: 1rem; + // opacity: 0.81; + padding-top: 0.75rem; + padding-bottom: 0.55rem; + color: #212121; +} + +.description-subtitle, +.dmp-subtitle { + display: flex; + flex-direction: row; + text-align: left; + font-weight: 400; + font-family: "Roboto", sans-serif; + font-size: 0.875rem; + opacity: 1; + align-items: center; + color: #848484; +} + +.dmp-title-draft, +.description-title-draft { + text-align: left; + font-weight: 600; + font-family: "Roboto", sans-serif; + font-size: 1rem; + // opacity: 0.81; + padding-top: 0.75rem; + padding-bottom: 0.55rem; + color: #f16868; +} + +.icon-align { + display: inline-flex; + vertical-align: middle; + // line-height: .9em; + // padding-bottom: 0.4rem; +} + +.dmp-subtitle, +.description-subtitle { + + .icon-align { + display: inline-flex; + vertical-align: top; + margin-right: .2rem; + line-height: .95em; + } +} + +.description-card-actions, +.dmp-card-actions { + display: flex; + flex-direction: row; + border-top: 1px solid #dbdbdb; + line-height: 4; + color: #848484; +} + +.description-card-actions a, +.dmp-card-actions a { + color: #848484 !important; + text-decoration: none !important; +} + +.description-card-actions a:hover, +.dmp-card-actions a:hover { + color: var(--primary-color) !important; +} + +.dmp-description-descriptions-title { + color: #000000; + opacity: 0.6; + padding-top: 1.5rem; + padding-bottom: 0.8rem; +} + +.dmp-description-descriptions-name { + color: #000000; + opacity: 0.6; + font-weight: 700; +} + +.show-more { + color: black !important; +} + +.show-more:hover { + color: var(--primary-color) !important; +} + +.btn-load-more { + border: 2px solid #212121; + border-radius: 30px; + opacity: 1; + min-width: 132px; + width: auto; + height: 40px; + margin-top: 4.125rem; +} + +.btn-load-more:hover { + background-color: black; + color: white; +} + +.draft { + color: #f16868; +} + +.pointer { + cursor: pointer; +} + +::ng-deep .mat-menu-panel { + max-width: 282px !important; +} diff --git a/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.ts b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.ts new file mode 100644 index 000000000..77ad53d61 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/listing/listing-item/description-listing-item.component.ts @@ -0,0 +1,268 @@ +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { UntypedFormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { Role } from '@app/core/common/enum/role'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; +import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { takeUntil } from 'rxjs/operators'; +import { DescriptionStatus } from '../../../../core/common/enum/description-status'; +import { Description } from '@app/core/model/description/description'; +import { DescriptionService } from '@app/core/services/description/description.service'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { Guid } from '@common/types/guid'; + +@Component({ + selector: 'app-description-listing-item-component', + templateUrl: './description-listing-item.component.html', + styleUrls: ['./description-listing-item.component.scss'] +}) +export class DescriptionListingItemComponent extends BaseComponent implements OnInit { + + @Input() description: Description; + @Input() showDivider: boolean = true; + @Input() isPublic: boolean = false; + @Output() onClick: EventEmitter = new EventEmitter(); + + isDraft: boolean; + isDeleted: boolean; + isUserOwner: boolean; + + constructor( + private router: Router, + public enumUtils: EnumUtils, + private descriptionService: DescriptionService, + public dialog: MatDialog, + private language: TranslateService, + private authService: AuthService, + private uiNotificationService: UiNotificationService, + private lockService: LockService, + private location: Location, + private httpClient: HttpClient, + private matomoService: MatomoService, + private fileUtils: FileUtils + ) { + super(); + } + + ngOnInit() { + this.matomoService.trackPageView('Description Listing Item'); + if (this.description.isActive === IsActive.Inactive) { + this.isDeleted = true; + } else if (this.description.status === DescriptionStatus.Draft) { + this.isDraft = true; + this.isDeleted = false; + this.setIsUserOwner(); + } else { + this.isDraft = false; + this.isDeleted = false; + this.setIsUserOwner(); + } + } + + setIsUserOwner() { + if (this.description) { + const principalId: string = this.authService.userId()?.toString(); + //TODO: add user to description objects + //if (principalId) this.isUserOwner = !!this.description.users.find(x => (x.role === Role.Owner) && (principalId === x.id)); + } + } + + public isAuthenticated(): boolean { + return this.authService.currentAccountIsAuthenticated(); + } + + getItemLink(): string[] { + // return this.isPublic ? [`/descriptions/publicEdit/${this.description.id}`] : [`/descriptions/edit/${this.description.id}`]; + return this.isPublic ? ['/descriptions/publicOverview/' + this.description.id] : ['/descriptions/overview/' + this.description.id]; + } + + getDmpLink(): string[] { + return this.isPublic ? [`/explore-plans/publicOverview/${this.description.dmp.id}`] : [`/plans/edit/${this.description.dmp.id}`]; + } + + downloadPDF(description: Description): void { + //TODO: add file transformer service + // this.descriptionService.downloadPDF(description.id as string) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/pdf' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('descriptions', "pdf", description.id); + // }); + } + + downloadDOCX(description: Description): void { + //TODO: add file transformer service + // this.descriptionService.downloadDOCX(description.id as string) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/msword' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('descriptions', "docx", description.id); + // }); + + } + + downloadXML(description: Description): void { + //TODO: add file transformer service + // this.descriptionService.downloadXML(description.id as string) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/xml' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('descriptions', "xml", description.id); + // }); + } + + openShareDialog(dmpRowId: any, dmpRowName: any) { + // TODO: This is a shared component. Put it in a seperate module. + const dialogRef = this.dialog.open(DmpInvitationDialogComponent, { + // height: '250px', + // width: '700px', + autoFocus: false, + restoreFocus: false, + data: { + dmpId: dmpRowId, + dmpName: dmpRowName + } + }); + } + + openDmpSearchDialogue(description: Description) { + // TODO: add this. + // const formControl = new UntypedFormControl(); + // const dialogRef = this.dialog.open(DescriptionCopyDialogueComponent, { + // width: '500px', + // restoreFocus: false, + // data: { + // formControl: formControl, + // descriptionId: description.id, + // descriptionProfileId: description.profile.id, + // descriptionProfileExist: false, + // confirmButton: this.language.instant('DATASET-WIZARD.DIALOGUE.COPY'), + // cancelButton: this.language.instant('DATASET-WIZARD.DIALOGUE.CANCEL') + // } + // }); + + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)) + // .subscribe(result => { + // if (result && result.descriptionProfileExist) { + // const newDmpId = result.formControl.value.id; + // this.router.navigate(['/descriptions/copy/' + result.descriptionId], { queryParams: { newDmpId: newDmpId } }); + // } + // }); + } + + deleteClicked(id: Guid) { + this.lockService.checkLockStatus(id.toString()).pipe(takeUntil(this._destroyed)) + .subscribe(lockStatus => { + if (!lockStatus) { + this.openDeleteDialog(id); + } else { + this.openLockedByUserDialog(); + } + }); + } + + openDeleteDialog(id: Guid): void { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.DELETE'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.descriptionService.delete(id) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onDeleteCallbackSuccess(), + error => this.onDeleteCallbackError(error) + ); + } + }); + } + + openLockedByUserDialog() { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '400px', + restoreFocus: false, + data: { + message: this.language.instant('DATASET-WIZARD.ACTIONS.LOCK') + } + }); + } + + reloadPage(): void { + const path = this.location.path(); + this.router.navigateByUrl('/reload', { skipLocationChange: true }).then(() => { + this.router.navigate([path]); + }); + } + + onDeleteCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success); + this.reloadPage(); + } + + onDeleteCallbackError(error) { + this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error); + } + + roleDisplay(value: any) { + const principalId: string = this.authService.userId()?.toString(); + let role: number; + if (principalId) { + value.forEach(element => { + if (principalId === element.id) { + role = element.role; + } + }); + } + if (role === 0) { + return this.language.instant('DMP-LISTING.OWNER'); + } + else if (role === 1) { + return this.language.instant('DMP-LISTING.MEMBER'); + } + else { + return this.language.instant('DMP-LISTING.OWNER'); + } + } + + isUserDescriptionRelated() { + //TODO: add user to description objects + const principalId: string = this.authService.userId()?.toString(); + let isRelated: boolean = false; + // if (this.description && principalId) { + // this.description.users.forEach(element => { + // if (element.id === principalId) { + // isRelated = true; + // } + // }) + // } + return isRelated; + } +} diff --git a/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.html b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.html new file mode 100644 index 000000000..92c068c65 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.html @@ -0,0 +1,186 @@ +
+
+
+ + chevron_left +

{{'DMP-WIZARD.ACTIONS.BACK' | translate}}

+
+
+
+
+ +

{{ dataset.label }}

+
+
+
+

+ {{ roleDisplayFromList(dataset.users) }} +

+
+ . + + +
+ public + {{'DMP-OVERVIEW.PUBLIC' | translate}} +
+ . + + +
+ lock_outline + {{'DMP-OVERVIEW.LOCKED' | translate}} +
+ . +
{{'GENERAL.STATUSES.EDIT' | translate}} : + + {{dataset.modified | dateTimeCultureFormatter: "d MMMM y"}} +
+
+
+ check + {{'TYPES.DATASET-STATUS.FINALISED' | translate}} +
+
+
+
+ + + +
+
{{'DATASET-LISTING.TOOLTIP.PART-OF' | translate}}
+
+ +
+ +
+
{{'DMP-OVERVIEW.GRANT' | translate}}
+
{{ dataset.grant.label }}
+
+ +
{{'DMP-OVERVIEW.RESEARCHERS' | translate}}
+
+
+ + +
 
+
{{ researcher.name }},
+
{{ researcher.name }}
+
+
+ +
{{ researcher.name }},
+
{{ researcher.name }}
+
+
+ horizontal_rule +
+ +
{{'DATASET-LISTING.COLUMNS.DESCRIPTION' | translate}}
+
+

+
+
+ horizontal_rule +
+
+
+
+
+
+ +

{{ 'DMP-LISTING.ACTIONS.FINALIZE' | translate }}

+
+
+
+
+
+
+ +

{{ 'DATASET-WIZARD.ACTIONS.REVERSE' | translate }}

+
+
+ +

+ {{ 'DMP-LISTING.ACTIONS.EXPORT' | translate }}

+
+ + + + + + + + +
+
+
+

{{ 'DATASET-OVERVIEW.DESCRIPTION-AUTHORS' | translate }}

+
+
+
+
+ +
+

{{ user.name }} + + ({{ 'DMP-OVERVIEW.YOU' | translate }}) +

+

{{ roleDisplay(user) }}

+
+
+ +
+
+
+ +
+
+
+
+
+
+
diff --git a/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.scss b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.scss new file mode 100644 index 000000000..4d5c9c4c6 --- /dev/null +++ b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.scss @@ -0,0 +1,301 @@ +.container-fluid { + margin: 2em 4em; + padding: 2em; +} + +.dataset-overview { + // margin-top: 5rem; +} + +// ********ICONS******** + +.back-icon { + opacity: 0.4; +} + +.mat-mini-fab { + width: 2.5em; + height: 2.5em; + color: #212121; + background-color: var(--secondary-color); +} + +.mat-mini-fab-icon, +.status-icon { + font-size: 1.2em; +} + +.actions-btn:hover, +.finalize-btn:hover { + background-color: var(--primary-color); + color: #ffffff; +} + +.status-icon { + color: #a7a7a7; +} + +.check-icon { + font-weight: bold; +} + +.account-icon { + font-size: 2.5em; +} + +// ********BUTTONS******** + +.version-btn { + // width: 6.7em; + height: 1.8em; + border: 1px solid #707070; + border-radius: 4px; + background-color: transparent; +} + +.id-btn { + background: url("../../../../assets/images/NoPath.png") no-repeat center; + width: 1em; + margin-right: 0.3em; + align-self: center; +} + +.dmp-btn { + width: 35em; + min-height: 2.3em; + background-color: var(--primary-color); + border-radius: 4px; + flex-direction: row; + justify-content: space-between; + border: none; +} + +.dmp-btn, +.dmp-btn > mat-icon { + color: #ffffff; + // opacity: 0.8; +} + +.show-more-btn { + width: 31.6em; + padding: 0 1em; + background-color: #ffffff00; + color: var(--primary-color); + font-weight: 700; +} + +.frame-btn { + border: 1px solid #212121; + color: black; + background: #ffffff; +} + +.finalize-btn { + // border: 1px solid var(--secondary-color); + background: #f5db71; +} + +.frame-btn, +.finalize-btn { + box-shadow: 0px 2px 6px #00000029; +} + +.remove-btn { + border: none; + background-color: transparent; + font-size: 0.875em; + font-weight: bold; + margin-left: auto; +} + +.invite-btn { + width: 9.4em; + height: 2.9em; + background: #ffffff; + box-shadow: 0px 3px 6px #1e202029; + border: 2px solid #212121; + border-radius: 30px; +} + +.account_btn { + background: white; + color: #d5d5d5; + border: none; + height: 2.9em; +} + +// ********TEXT******** + +.dataset-logo { + width: 6em; + height: 2.6em; + background: var(--secondary-color); + border-radius: 4px; + font-size: 0.875em; + // color: #212121; + // color: black; + // opacity: 0.75; +} + +.label-txt { + font-size: 0.875em; +} + +.label2-txt { + font-size: 1em; +} + +.label-txt, +.label2-txt { + color: #848484; + font-weight: 400; +} + +.dataset-label { + font-weight: bold; + width: auto; +} + +.uppercase { + text-transform: uppercase; +} + +.researcher { + font-size: 0.875em; + color: var(--primary-color); + padding-right: 0.5em; + align-self: center; +} + +.header { + opacity: 0.6; + margin-top: 1em; + margin-bottom: 0.5em; +} + +.dataset-label, +.header { + font-size: 1.25em; + color: #212121; +} + +.desc-txt { + width: 48.25em; + font-size: 1em; + color: #212121; + margin-bottom: 1.875em; +} + +.dmp-btn-label { + margin-right: 1em; + overflow: hidden; + color: #ffffff; + opacity: 0.8; + text-align: left; +} + +.doi-label { + font-size: 1em; + color: #212121; + opacity: 0.6; + margin-bottom: 0.3em; +} + +.doi-txt { + font-size: 0.8em; + letter-spacing: 0.009em; + color: #7d7d7d; + width: 12em; + height: 1em; + overflow: hidden; + border: none; + padding: 0px; +} + +.doi-panel { + height: 3.5em; + background: #fafafa; + border: 1px solid #d1d1d1; + border-radius: 4px; + flex-direction: row; + justify-content: space-between; +} + +.doi-link { + color: white; +} + +.frame { + background: #ffffff; + box-shadow: 0px 1px 5px #00000026; + border-radius: 4px; + overflow: hidden; +} + +.frame-txt { + color: #000000; +} + +.frame-txt, +.finalize-txt { + font-size: 0.75em; + font-weight: bold; + letter-spacing: 0px; + text-transform: uppercase; + cursor: pointer; +} + +.hr-line { + border: 1px solid #dbdbdb; + // width: 274px; + // width: 17em; + width: 100%; +} + +.authors { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; +} + +.authors-label { + font-size: 0.875em; + color: #212121; + height: 1.4em; + margin-bottom: 0px; +} + +.authors-role { + font-size: 0.875em; + color: #a8a8a8; + height: 1.4em; + margin-bottom: 0px; +} + +// ********CENTER ELEMENTS******** + +.mat-mini-fab, +.mat-mini-fab-icon, +.actions-btn, +.status-icon, +.dataset-logo, +.frame-btn, +.finalize-btn { + display: flex; + justify-content: center; + align-items: center; +} + +.dataset-label, +.dmp-btn, +.doi-panel, +.researcher { + display: flex; + align-items: center; +} + +.show-more-btn { + display: flex; + justify-content: center; +} diff --git a/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.ts b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.ts new file mode 100644 index 000000000..9c372d3ab --- /dev/null +++ b/dmp-frontend/src/app/ui/description/overview/dataset-overview.component.ts @@ -0,0 +1,549 @@ +import { Component, OnInit } from '@angular/core'; +import { DatasetOverviewModel } from '@app/core/model/dataset/dataset-overview'; +import { BaseComponent } from '@common/base/base.component'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { Location } from '@angular/common'; +import { HttpClient } from '@angular/common/http'; +import { UntypedFormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; +import { DmpStatus } from '@app/core/common/enum/dmp-status'; +import { Role } from '@app/core/common/enum/role'; +import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; +import { DmpOverviewModel } from '@app/core/model/dmp/dmp-overview'; +import { ResearcherModel } from '@app/core/model/researcher/researcher'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DatasetService } from '@app/core/services/dataset/dataset.service'; +import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { LockService } from '@app/core/services/lock/lock.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { UserServiceOld } from '@app/core/services/user/user.service-old'; +import { FileUtils } from '@app/core/services/utilities/file-utils.service'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; +import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; +import { filter, takeUntil } from 'rxjs/operators'; +import { DatasetCopyDialogueComponent } from '../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; + + +@Component({ + selector: 'app-dataset-overview', + templateUrl: './dataset-overview.component.html', + styleUrls: ['./dataset-overview.component.scss'] +}) +export class DatasetOverviewComponent extends BaseComponent implements OnInit { + + dataset: DatasetOverviewModel; + // datasetWizardEditorModel: DatasetWizardEditorModel; + datasetWizardModel: DatasetWizardModel; + isNew = true; + isFinalized = false; + isPublicView = true; + hasPublishButton: boolean = true; + // breadCrumbs: Observable = observableOf(); + isUserOwner: boolean; + expand = false; + researchers: ResearcherModel[]; + users: UserInfoListingModel[]; + lockStatus: Boolean; + + constructor( + private route: ActivatedRoute, + private router: Router, + private datasetService: DatasetService, + private translate: TranslateService, + private authentication: AuthService, + private dialog: MatDialog, + private language: TranslateService, + private uiNotificationService: UiNotificationService, + private configurationService: ConfigurationService, + private oauth2DialogService: Oauth2DialogService, + private userService: UserServiceOld, + private dmpService: DmpService, + private location: Location, + private datasetWizardService: DatasetWizardService, + private lockService: LockService, + private httpClient: HttpClient, + private matomoService: MatomoService, + private fileUtils: FileUtils + ) { + super(); + } + + ngOnInit() { + this.matomoService.trackPageView('Dataset Overview'); + // Gets dataset data using parameter id + this.route.params + .pipe(takeUntil(this._destroyed)) + .subscribe((params: Params) => { + const itemId = params['id']; + const publicId = params['publicId']; + if (itemId != null) { + this.isNew = false; + this.isPublicView = false; + this.datasetService.getOverviewSingle(itemId) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.dataset = data; + this.researchers = this.dataset.dmp.researchers; + this.users = this.dataset.dmp.users; + this.checkLockStatus(this.dataset.id); + this.setIsUserOwner(); + // const breadCrumbs = []; + // breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), url: "/datasets" }); + // breadCrumbs.push({ parentComponentName: 'DatasetListingComponent', label: this.dataset.label, url: '/datasets/overview/' + this.dataset.id }); + // this.breadCrumbs = observableOf(breadCrumbs); + }, (error: any) => { + if (error.status === 404) { + return this.onFetchingDeletedCallbackError('/datasets/'); + } + if (error.status === 403) { + return this.onFetchingForbiddenCallbackError('/datasets/'); + } + }); + } + else if (publicId != null) { + this.isNew = false; + this.isFinalized = true; + this.isPublicView = true; + this.datasetService.getOverviewSinglePublic(publicId) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.dataset = data; + this.researchers = this.dataset.dmp.researchers; + this.users = this.dataset.dmp.users; + // const breadCrumbs = []; + // breadCrumbs.push({ parentComponentName: null, label: this.language.instant('NAV-BAR.PUBLIC DATASETS'), url: "/explore" }); + // breadCrumbs.push({ parentComponentName: 'DatasetListingComponent', label: this.dataset.label, url: '/datasets/publicOverview/' + this.dataset.id }); + // this.breadCrumbs = observableOf(breadCrumbs); + }, (error: any) => { + if (error.status === 404) { + return this.onFetchingDeletedCallbackError('/explore'); + } + if (error.status === 403) { + return this.onFetchingForbiddenCallbackError('/explore'); + } + }); + } + }); + } + + checkLockStatus(id: string) { + this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) + .subscribe(lockStatus => { + this.lockStatus = lockStatus + if (lockStatus) { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('DATASET-OVERVIEW.LOCKED.TITLE'), + message: this.language.instant('DATASET-OVERVIEW.LOCKED.MESSAGE') + }, maxWidth: '30em' + }); + } + }); + } + + onFetchingDeletedCallbackError(redirectRoot: string) { + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.DELETED-DATASET'), SnackBarNotificationLevel.Error); + this.router.navigate([redirectRoot]); + } + + onFetchingForbiddenCallbackError(redirectRoot: string) { + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-OVERVIEW.ERROR.FORBIDEN-DATASET'), SnackBarNotificationLevel.Error); + this.router.navigate([redirectRoot]); + } + + goBack(): void { + this.location.back(); + } + + reloadPage(): void { + const path = this.location.path(); + this.router.navigateByUrl('/reload', { skipLocationChange: true }).then(() => this.router.navigate([path])); + } + + setIsUserOwner() { + if (this.dataset) { + const principalId: string = this.authentication.userId()?.toString(); + if (principalId) this.isUserOwner = !!this.dataset.users.find(x => (x.role === Role.Owner) && (principalId === x.id)); + } + } + + isUserAuthor(userId: string): boolean { + if (this.isAuthenticated()) { + const principalId: string = this.authentication.userId()?.toString(); + return userId === principalId; + } else return false; + } + + isUserDatasetRelated() { + const principalId: string = this.authentication.userId()?.toString(); + let isRelated: boolean = false; + if (this.dataset && principalId) { + this.dataset.users.forEach(element => { + if (element.id === principalId) { + isRelated = true; + } + }) + } + return isRelated; + } + + roleDisplay(value: UserInfoListingModel) { + if (value.role === Role.Owner) { + return this.translate.instant('DMP-LISTING.OWNER'); + } else if (value.role === Role.Member) { + return this.translate.instant('DMP-LISTING.MEMBER'); + } else { + return this.translate.instant('DMP-LISTING.OWNER'); + } + } + + + roleDisplayFromList(value: UserInfoListingModel[]) { + const principalId: string = this.authentication.userId()?.toString(); + let role: number; + if (principalId) { + value.forEach(element => { + if (principalId === element.id) { + role = element.role; + } + }); + } + if (role === Role.Owner) { + return this.translate.instant('DMP-LISTING.OWNER'); + } else if (role === Role.Member) { + return this.translate.instant('DMP-LISTING.MEMBER'); + } else { + return this.translate.instant('DMP-LISTING.OWNER'); + } + } + + openShareDialog(rowId: any, rowName: any) { + const dialogRef = this.dialog.open(DmpInvitationDialogComponent, { + autoFocus: false, + restoreFocus: false, + data: { + dmpId: rowId, + dmpName: rowName + } + }); + } + + public isAuthenticated(): boolean { + return this.authentication.currentAccountIsAuthenticated(); + } + + isDraftDataset(dataset: DatasetOverviewModel) { + return dataset.status == DatasetStatus.Draft; + } + + isFinalizedDataset(dataset: DatasetOverviewModel) { + return dataset.status == DatasetStatus.Finalized; + } + + editClicked(dataset: DatasetOverviewModel) { + if (dataset.public) { + this.router.navigate(['/datasets/publicEdit/', dataset.id]); + // let url = this.router.createUrlTree(['/datasets/publicEdit/', dataset.id]); + // window.open(url.toString(), '_blank'); + } else { + this.router.navigate(['/datasets/edit/', dataset.id]); + // let url = this.router.createUrlTree(['/datasets/edit/', dataset.id]); + // let url = this.router.createUrlTree(['/plans/edit/', dataset.dmp.id], { queryParams: { dataset: dataset.id } }); + // window.open(url.toString(), '_blank'); + } + } + + deleteClicked() { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.DELETE'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.datasetService.delete(this.dataset.id) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => { + this.onDeleteCallbackSuccess(); + }, + error => this.onDeleteCallbackError(error) + ); + } + }); + } + + dmpClicked(dmp: DmpOverviewModel) { + if (this.isPublicView) { + this.router.navigate(['/explore-plans/publicOverview/' + dmp.id]); + } else { + this.router.navigate(['/plans/overview/' + dmp.id]); + } + } + + onDeleteCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success); + this.router.navigate(['/datasets']); + } + + onDeleteCallbackError(error) { + this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error); + } + + onUpdateCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); + this.reloadPage(); + } + + onUpdateCallbackError(error) { + this.uiNotificationService.snackBarNotification(error.error.message ? this.tryTranslate(error.error.message) : this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Error); + } + tryTranslate(errorMessage: string): string { + return errorMessage.replace('Field value of', this.language.instant('Field value of')) + .replace('must be filled', this.language.instant('must be filled')); + } + + public getOrcidPath(): string { + return this.configurationService.orcidPath; + } + + isOrcid(reference: string) { + const head = reference.split(':')[0]; + return head === 'orcid'; + } + + getOrcidPathForResearcher(reference: string): string { + const path = this.getOrcidPath(); + const userId = reference.split(':')[1]; + return path + userId; + } + + downloadPDF(id: string) { + this.datasetService.downloadPDF(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/pdf' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "pdf", id); + }); + } + + downloadDocx(id: string) { + this.datasetService.downloadDocx(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/msword' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "docx", id); + }); + } + + downloadXml(id: string) { + this.datasetService.downloadXML(id) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + + FileSaver.saveAs(blob, filename); + this.matomoService.trackDownload('datasets', "xml", id); + }); + } + + //GK: NO + // downloadJson(id: string) { + // this.datasetService.downloadJson(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/json' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + // FileSaver.saveAs(blob, filename); + // }) + // } + + openDmpSearchDialogue() { + const formControl = new UntypedFormControl(); + const dialogRef = this.dialog.open(DatasetCopyDialogueComponent, { + width: '500px', + restoreFocus: false, + data: { + formControl: formControl, + datasetId: this.dataset.id, + datasetProfileId: this.dataset.datasetTemplate.id, + datasetProfileExist: false, + confirmButton: this.language.instant('DATASET-WIZARD.DIALOGUE.COPY'), + cancelButton: this.language.instant('DATASET-WIZARD.DIALOGUE.CANCEL') + } + }); + + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (result && result.datasetProfileExist) { + const newDmpId = result.formControl.value.id; + this.router.navigate(['/datasets/copy/' + result.datasetId], { queryParams: { newDmpId: newDmpId } }); + // let url = this.router.createUrlTree(['/datasets/copy/', result.datasetId, { newDmpId: newDmpId }]) + // window.open(url.toString(), '_blank') + } + }); + } + + updateUsers() { + return this.dmpService.updateUsers(this.dataset.dmp.id, this.users).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => { + this.onUpdateCallbackSuccess(); + }, + error => this.onUpdateCallbackError(error) + ); + } + + removeUserFromDmp(user: UserInfoListingModel) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-USER'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.REMOVE'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: false + } + }); + dialogRef.afterClosed().subscribe(result => { + if (result) { + const index = this.users.findIndex(x => x.id === user.id); + if (index > -1) { + this.users.splice(index, 1); + } + this.updateUsers(); + } + }); + } + + showPublishButton(dataset: DatasetOverviewModel) { + return this.isFinalizedDataset(dataset) && !dataset.public && this.hasPublishButton; + } + + // publish(id: String) { + // const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + // maxWidth: '500px', + // restoreFocus: false, + // data: { + // message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.PUBLISH-ITEM'), + // privacyPolicyNames: this.language.instant('GENERAL.CONFIRMATION-DIALOG.PRIVACY-POLICY-NAMES'), + // confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + // cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + // isDeleteConfirmation: false + // } + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // this.datasetService.publish(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(() => { + // this.hasPublishButton = false; + // this.reloadPage(); + // }); + // } + // }); + // } + + finalize(dataset: DatasetOverviewModel) { + + + this.dialog.open(ConfirmationDialogComponent, { + data: { + message: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.MESSAGE'), + confirmButton: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.CONFIRM'), + cancelButton: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.CANCEL'), + }, + maxWidth: '30em' + }) + .afterClosed() + .pipe( + filter(x => x), + takeUntil(this._destroyed) + ) + .subscribe(_ => { + this.router.navigate(['datasets', 'edit', dataset.id, 'finalize']); + }) + + + + + + // const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + // restoreFocus: false, + // data: { + // message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.FINALIZE-ITEM'), + // confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), + // cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), + // isDeleteConfirmation: false + // } + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // this.datasetWizardService.getSingle(dataset.id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(data => { + // this.datasetWizardModel = data; + // this.datasetWizardModel.status = DatasetStatus.Finalized; + // this.datasetWizardService.createDataset(this.datasetWizardModel) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // data => this.onUpdateCallbackSuccess(), + // error => this.onUpdateCallbackError(error) + // ); + // }); + // } + // }); + } + + hasReversableStatus(dataset: DatasetOverviewModel): boolean { + return dataset.dmp.status == DmpStatus.Draft && dataset.status == DatasetStatus.Finalized + } + + reverse(dataset: DatasetOverviewModel) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.UNFINALIZE-ITEM'), + confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), + cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), + isDeleteConfirmation: false + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.datasetWizardService.getSingle(dataset.id) + .pipe(takeUntil(this._destroyed)) + .subscribe(data => { + this.datasetWizardModel = data; + this.datasetWizardModel.status = DatasetStatus.Draft; + this.datasetWizardService.createDataset(this.datasetWizardModel) + .pipe(takeUntil(this._destroyed)) + .subscribe( + data => this.onUpdateCallbackSuccess(), + error => this.onUpdateCallbackError(error) + ); + }); + } + }); + } + +} diff --git a/dmp-frontend/src/app/ui/description/overview/dataset-overview.module.ts b/dmp-frontend/src/app/ui/description/overview/dataset-overview.module.ts new file mode 100644 index 000000000..293bffdfb --- /dev/null +++ b/dmp-frontend/src/app/ui/description/overview/dataset-overview.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { FormattingModule } from '@app/core/formatting.module'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; +import { ExportMethodDialogModule } from '@app/library/export-method-dialog/export-method-dialog.module'; +import { UrlListingModule } from '@app/library/url-listing/url-listing.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { DatasetOverviewComponent } from './dataset-overview.component'; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + UrlListingModule, + ConfirmationDialogModule, + ExportMethodDialogModule, + FormattingModule, + AutoCompleteModule + ], + declarations: [ + DatasetOverviewComponent + ] +}) +export class DatasetOverviewModule { } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts index c5df69442..edc81cbec 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts @@ -8,11 +8,10 @@ import { MatSort } from '@angular/material/sort'; import { ActivatedRoute, Router } from '@angular/router'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; -import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; import { GrantListingModel } from '@app/core/model/grant/grant-listing'; import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { DmpService } from '@app/core/services/dmp/dmp.service'; +import { DmpService, DmpServiceNew } from '@app/core/services/dmp/dmp.service'; import { GrantService } from "@app/core/services/grant/grant.service"; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; @@ -31,6 +30,9 @@ import { Observable, of as observableOf } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { DmpCriteriaDialogComponent } from './criteria/dmp-criteria-dialog.component'; import { DmpUploadDialogue } from './upload-dialogue/dmp-upload-dialogue.component'; +import { DmpLookup } from '@app/core/query/dmp.lookup'; +import { Dmp } from '@app/core/model/dmp/dmp'; +import { nameof } from 'ts-simple-nameof'; @Component({ selector: 'app-dmp-listing-component', @@ -49,7 +51,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr showGrant: boolean; titlePrefix: string; totalCount: number; - listingItems: DmpListingModel[] = []; + listingItems: Dmp[] = []; allVersions: boolean = false; groupLabel: string; isPublic: boolean = false; @@ -73,6 +75,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr constructor( private dmpService: DmpService, + private dmpServiceNew: DmpServiceNew, private router: Router, private route: ActivatedRoute, public dialogAnimation: NgDialogAnimationService, @@ -233,13 +236,6 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr } public refresh(resetPages = false) { - // if (this._paginator.pageSize === undefined) this._paginator.pageSize = 10; - // if (resetPages) this._paginator.pageIndex = 0; - // const startIndex = this._paginator.pageIndex * this._paginator.pageSize; - - // let fields: Array = new Array(); - // if (this.sort && this.sort.active) { fields = this.sort.direction === 'asc' ? ['+' + this.sort.active] : ['-' + this.sort.active]; } - // fields.push('-modified'); const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; this.startIndex = 0; @@ -247,47 +243,81 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr this.setPublicCriteria(); request.criteria = this.criteria; - this.dmpService.getPaged(request, "listing").pipe(takeUntil(this._destroyed)).subscribe(result => { - if (!result) { return []; } - result.data.map(item => { - item['datasets'].map(dmp => { - dmp.url = 'datasets/edit/' + dmp.url; - dmp.all = 'datasets/dmp/' + item.id; - return dmp; - }); - return item; - }); - this.listingItems = result.data; + let lookup: DmpLookup = new DmpLookup(); + lookup.project = { + fields: [ + nameof(x => x.id), + nameof(x => x.label), + ] + }; + this.dmpServiceNew.query(lookup).pipe(takeUntil(this._destroyed)) + .subscribe(result => { + if (!result) { return; } + // result.data.map(item => { + // item['datasets'].map(dmp => { + // dmp.url = 'datasets/edit/' + dmp.url; + // dmp.all = 'datasets/dmp/' + item.id; + // return dmp; + // }); + // return item; + // }); + this.listingItems = result.items; this.hasListingItems = true; if (!this.isPublic && this.listingItems.length === 0 && !this.hasCriteria() && !this.hasLikeCriteria()) { this.openTour(); } - this.totalCount = result.totalCount; + this.totalCount = result.count; }); + + // const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; + // this.startIndex = 0; + + // const request = new DataTableRequest(this.startIndex, this.pageSize, { fields: fields }); + // this.setPublicCriteria(); + // request.criteria = this.criteria; + + // this.dmpService + // this.dmpService.getPaged(request, "listing").pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (!result) { return []; } + // result.data.map(item => { + // item['datasets'].map(dmp => { + // dmp.url = 'datasets/edit/' + dmp.url; + // dmp.all = 'datasets/dmp/' + item.id; + // return dmp; + // }); + // return item; + // }); + // this.listingItems = result.data; + // this.hasListingItems = true; + // if (!this.isPublic && this.listingItems.length === 0 && !this.hasCriteria() && !this.hasLikeCriteria()) { + // this.openTour(); + // } + // this.totalCount = result.totalCount; + // }); } public loadMore() { - this.startIndex = this.startIndex + this.pageSize; - // const fields: Array = ["-modified"]; - const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; - const request = new DataTableRequest(this.startIndex, this.pageSize, { fields: fields }); - this.setPublicCriteria(); - request.criteria = this.criteria; + // this.startIndex = this.startIndex + this.pageSize; + // // const fields: Array = ["-modified"]; + // const fields: Array = [((this.formGroup.get('order').value === 'status') || (this.formGroup.get('order').value === 'label') ? '+' : "-") + this.formGroup.get('order').value]; + // const request = new DataTableRequest(this.startIndex, this.pageSize, { fields: fields }); + // this.setPublicCriteria(); + // request.criteria = this.criteria; - this.dmpService.getPaged(request, "listing").pipe(takeUntil(this._destroyed)).subscribe(result => { - if (!result) { return []; } - result.data.map(item => { - item['datasets'].map(dmp => { - dmp.url = 'datasets/edit/' + dmp.url; - dmp.all = 'datasets/dmp/' + item.id; - return dmp; - }); - return item; - }); - // this.listingItems = this.listingItems.concat(result.data); - this.listingItems = this.mergeTwoSortedLists(this.listingItems, result.data, this.formGroup.get('order').value); - this.hasListingItems = true; - }); + // this.dmpService.getPaged(request, "listing").pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (!result) { return []; } + // result.data.map(item => { + // item['datasets'].map(dmp => { + // dmp.url = 'datasets/edit/' + dmp.url; + // dmp.all = 'datasets/dmp/' + item.id; + // return dmp; + // }); + // return item; + // }); + // // this.listingItems = this.listingItems.concat(result.data); + // this.listingItems = this.mergeTwoSortedLists(this.listingItems, result.data, this.formGroup.get('order').value); + // this.hasListingItems = true; + // }); } pageThisEvent(event) { @@ -315,7 +345,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr this.refresh(); } - // rowClicked(dmp: DmpListingModel) { + // rowClicked(dmp: Dmp) { // this.router.navigate(['/plans/overview/' + dmp.id]); // } @@ -464,62 +494,62 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr return document.getElementById("main-page").scrollHeight > document.documentElement.clientHeight } - private mergeTwoSortedLists(arr1: DmpListingModel[], arr2: DmpListingModel[], order: string): DmpListingModel[] { - let merged = []; - let index1 = 0; - let index2 = 0; - let current = 0; + // private mergeTwoSortedLists(arr1: Dmp[], arr2: Dmp[], order: string): Dmp[] { + // let merged = []; + // let index1 = 0; + // let index2 = 0; + // let current = 0; - while (current < (arr1.length + arr2.length)) { + // while (current < (arr1.length + arr2.length)) { - let isArr1Depleted = index1 >= arr1.length; - let isArr2Depleted = index2 >= arr2.length; + // let isArr1Depleted = index1 >= arr1.length; + // let isArr2Depleted = index2 >= arr2.length; - if (order === 'modified') { - if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].modifiedTime) > new Date(arr2[index2].modifiedTime)))) { - merged[current] = arr1[index1]; - index1++; - } else { - merged[current] = arr2[index2]; - index2++; - } - } else if (order === 'created') { - if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].creationTime) > new Date(arr2[index2].creationTime)))) { - merged[current] = arr1[index1]; - index1++; - } else { - merged[current] = arr2[index2]; - index2++; - } - } else if (order === 'label') { - if (!isArr1Depleted && (isArr2Depleted || (arr1[index1].label.localeCompare(arr2[index2].label)))) { - merged[current] = arr1[index1]; - index1++; - } else { - merged[current] = arr2[index2]; - index2++; - } - } else if (order === 'status') { - if (!isArr1Depleted && (isArr2Depleted || (arr1[index1].status < arr2[index2].status))) { - merged[current] = arr1[index1]; - index1++; - } else { - merged[current] = arr2[index2]; - index2++; - } - } else if (order === "publishedAt") { - if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].publishedAt) > new Date(arr2[index2].publishedAt)))) { - merged[current] = arr1[index1]; - index1++; - } else { - merged[current] = arr2[index2]; - index2++; - } - } - current++; - } - return merged; - } + // if (order === 'modified') { + // if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].modifiedTime) > new Date(arr2[index2].modifiedTime)))) { + // merged[current] = arr1[index1]; + // index1++; + // } else { + // merged[current] = arr2[index2]; + // index2++; + // } + // } else if (order === 'created') { + // if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].creationTime) > new Date(arr2[index2].creationTime)))) { + // merged[current] = arr1[index1]; + // index1++; + // } else { + // merged[current] = arr2[index2]; + // index2++; + // } + // } else if (order === 'label') { + // if (!isArr1Depleted && (isArr2Depleted || (arr1[index1].label.localeCompare(arr2[index2].label)))) { + // merged[current] = arr1[index1]; + // index1++; + // } else { + // merged[current] = arr2[index2]; + // index2++; + // } + // } else if (order === 'status') { + // if (!isArr1Depleted && (isArr2Depleted || (arr1[index1].status < arr2[index2].status))) { + // merged[current] = arr1[index1]; + // index1++; + // } else { + // merged[current] = arr2[index2]; + // index2++; + // } + // } else if (order === "publishedAt") { + // if (!isArr1Depleted && (isArr2Depleted || (new Date(arr1[index1].publishedAt) > new Date(arr2[index2].publishedAt)))) { + // merged[current] = arr1[index1]; + // index1++; + // } else { + // merged[current] = arr2[index2]; + // index2++; + // } + // } + // current++; + // } + // return merged; + // } public setDashboardTourDmpText(): void { this.dmpText = this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' + @@ -546,70 +576,4 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr public hasLikeCriteria(): boolean { return this.criteria.like !== undefined && this.criteria.like !== null; } -} - -// export class DmpDataSource extends DataSource { - -// totalCount = 0; - -// constructor( -// private _service: DmpService, -// private _paginator: MatPaginator, -// private _sort: MatSort, -// private _criteria: DmpCriteriaComponent, -// private itemId -// ) { -// super(); -// } - -// connect(): Observable { -// const displayDataChanges = [ -// this._paginator.page -// ]; - -// return Observable.merge(...displayDataChanges) -// .startWith(null) -// .switchMap(() => { -// const startIndex = this._paginator.pageIndex * this._paginator.pageSize; -// let fields: Array = new Array(); -// if (this._sort.active) { fields = this._sort.direction === 'asc' ? ['+' + this._sort.active] : ['-' + this._sort.active]; } -// const request = new DataTableRequest(startIndex, this._paginator.pageSize, { fields: fields }); -// request.criteria = this._criteria.formGroup.value; -// if (this.itemId) { -// request.criteria.groupIds = [this.itemId]; -// request.criteria.allVersions = true; -// } -// return this._service.getPaged(request, "listing"); -// }) -// /*.catch((error: any) => { -// this._snackBar.openFromComponent(SnackBarNotificationComponent, { -// data: { message: 'GENERAL.SNACK-BAR.FORMS-BAD-REQUEST', language: this._languageService }, -// duration: 3000, -// extraClasses: ['snackbar-warning'] -// }); -// return Observable.of(null); -// })*/ -// .map(result => { -// result.data = result.data; -// return result; -// }) -// .map(result => { -// return result; -// }) -// .map(result => { -// if (!result) { return []; } -// if (this._paginator.pageIndex === 0) { this.totalCount = result.totalCount; } -// return result.data.map(item => { -// item['datasets'].map(dmp => { -// dmp.url = 'datasets/edit/' + dmp.url; -// dmp.all = 'datasets/dmp/' + item.id; -// return dmp; -// }); -// return item; -// }); -// }); -// } - -// disconnect() { -// } -// } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.html b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.html index 4736f77ff..6174ecdf3 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.html +++ b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.html @@ -2,30 +2,30 @@
{{ 'DMP-LISTING.DMP' | translate }}
-
{{ 'DMP-LISTING.EDITED' | translate }}: {{ dmp.modifiedTime | dateTimeCultureFormatter: "d MMMM y" }}
+
{{ 'DMP-LISTING.EDITED' | translate }}: {{ dmp.updatedAt | dateTimeCultureFormatter: "d MMMM y" }}
{{ 'DMP-LISTING.PUBLISHED' | translate }}: {{ dmp.publishedAt | dateTimeCultureFormatter: "d MMMM y" }}
{{dmp.label}}
{{ roleDisplay(dmp.users) }} . - public{{'TYPES.DMP-VISIBILITY.PUBLIC' | translate}} - done{{ enumUtils.toDmpStatusString(dmp.status) }} - create{{ enumUtils.toDmpStatusString(dmp.status) }} + public{{'TYPES.DMP-VISIBILITY.PUBLIC' | translate}} + done{{ enumUtils.toDmpStatusString(dmp.status) }} + create{{ enumUtils.toDmpStatusString(dmp.status) }} . {{'DMP-LISTING.VERSION' | translate}} {{dmp.version}} . - {{ 'DMP-LISTING.GRANT' | translate }}: {{dmp.grant}} +
-
{{'DMP-LISTING.CONTAINED-DESCRIPTIONS' | translate}}: ({{ dmp.datasets.length }}) +
{{'DMP-LISTING.CONTAINED-DESCRIPTIONS' | translate}}: ({{ dmp.descriptions.length }})
-
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts index 835d4e85b..e2befe0e1 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/listing-item/dmp-listing-item.component.ts @@ -8,7 +8,7 @@ import { DmpBlueprintSectionFieldCategory } from '@app/core/common/enum/dmp-blue import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type'; import { Role } from '@app/core/common/enum/role'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, FieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; -import { DmpModel } from '@app/core/model/dmp/dmp'; +import { Dmp, DmpModel } from '@app/core/model/dmp/dmp'; import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; import { LockService } from '@app/core/services/lock/lock.service'; @@ -25,7 +25,6 @@ import * as FileSaver from 'file-saver'; import { map, takeUntil } from 'rxjs/operators'; import { nameof } from 'ts-simple-nameof'; import { DmpStatus } from '../../../../core/common/enum/dmp-status'; -import { DmpListingModel } from '../../../../core/model/dmp/dmp-listing'; import { AuthService } from '../../../../core/services/auth/auth.service'; import { CloneDialogComponent } from '../../clone/clone-dialog/clone-dialog.component'; import { DmpEditorModel } from '../../editor/dmp-editor.model'; @@ -34,6 +33,7 @@ import { FunderFormModel } from '../../editor/grant-tab/funder-form-model'; import { GrantTabModel } from '../../editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '../../editor/grant-tab/project-form-model'; import { DmpInvitationDialogComponent } from '../../invitation/dmp-invitation-dialog.component'; +import { DmpUserRole } from '@app/core/common/enum/dmp-user-role'; @Component({ selector: 'app-dmp-listing-item-component', @@ -42,16 +42,17 @@ import { DmpInvitationDialogComponent } from '../../invitation/dmp-invitation-di }) export class DmpListingItemComponent extends BaseComponent implements OnInit { - @Input() dmp: DmpListingModel; + @Input() dmp: Dmp; @Input() showDivider: boolean = true; @Input() isPublic: boolean; - @Output() onClick: EventEmitter = new EventEmitter(); + @Output() onClick: EventEmitter = new EventEmitter(); isDraft: boolean; isFinalized: boolean; isPublished: boolean; dmpModel: DmpEditorModel; dmpFormGroup: UntypedFormGroup; + dmpStatusEnum = DmpStatus; constructor( private router: Router, @@ -81,7 +82,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { this.isDraft = false; this.isFinalized = true; this.isPublished = false; - if (this.dmp.public == true) { this.isPublished = true } + // if (this.dmp.public == true) { this.isPublished = true } } } @@ -110,30 +111,18 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { this.router.navigate(['/datasets/dmp/' + rowId, { dmpLabel: rowLabel }]); } - viewVersions(rowId: String, rowLabel: String, dmp: DmpListingModel) { - if (dmp.public && !this.isUserOwner(dmp)) { - let url = this.router.createUrlTree(['/explore-plans/versions/', rowId, { groupLabel: rowLabel }]); - window.open(url.toString(), '_blank'); - // this.router.navigate(['/explore-plans/versions/' + rowId], { queryParams: { groupLabel: rowLabel } }); - } else { - let url = this.router.createUrlTree(['/plans/versions/', rowId, { groupLabel: rowLabel }]); - window.open(url.toString(), '_blank'); - // this.router.navigate(['/plans/versions/' + rowId], { queryParams: { groupLabel: rowLabel } }); - } + viewVersions(rowId: String, rowLabel: String, dmp: Dmp) { + // if (dmp.public && !this.isUserOwner(dmp)) { + // let url = this.router.createUrlTree(['/explore-plans/versions/', rowId, { groupLabel: rowLabel }]); + // window.open(url.toString(), '_blank'); + // // this.router.navigate(['/explore-plans/versions/' + rowId], { queryParams: { groupLabel: rowLabel } }); + // } else { + // let url = this.router.createUrlTree(['/plans/versions/', rowId, { groupLabel: rowLabel }]); + // window.open(url.toString(), '_blank'); + // // this.router.navigate(['/plans/versions/' + rowId], { queryParams: { groupLabel: rowLabel } }); + // } } - // itemClicked() { - // this.onClick.emit(this.dmp); - // } - - // grantClicked(grantId: String) { - // this.router.navigate(['/grants/edit/' + grantId]); - // } - - // datasetClicked(dmpId: string) { - // this.router.navigate(['/plans/edit/' + dmpId], { queryParams: { tab: "datasetDescriptions" } }); - // } - roleDisplay(value: any) { const principalId: string = this.authentication.userId()?.toString(); let role: number; @@ -159,41 +148,41 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { const principalId: string = this.authentication.userId()?.toString(); let isRelated: boolean = false; if (this.dmp && principalId) { - this.dmp.users.forEach(element => { - if (element.id === principalId) { - isRelated = true; - } - }) + // this.dmp.users.forEach(element => { + // if (element.id === principalId) { + // isRelated = true; + // } + // }) } return isRelated; } - cloneOrNewVersionClicked(dmp: DmpListingModel, isNewVersion: boolean) { - this.dmpService.getSingle(dmp.id).pipe(map(data => data as DmpModel)) - .pipe(takeUntil(this._destroyed)) - .subscribe(data => { - this.dmpModel = new DmpEditorModel(); - this.dmpModel.grant = new GrantTabModel(); - this.dmpModel.project = new ProjectFormModel(); - this.dmpModel.funder = new FunderFormModel(); - this.dmpModel.extraProperties = new ExtraPropertiesFormModel(); - this.dmpModel.fromModel(data); - this.dmpModel.status = DmpStatus.Draft; - this.dmpFormGroup = this.dmpModel.buildForm(); + cloneOrNewVersionClicked(dmp: Dmp, isNewVersion: boolean) { + // this.dmpService.getSingle(dmp.id).pipe(map(data => data as DmpModel)) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(data => { + // this.dmpModel = new DmpEditorModel(); + // this.dmpModel.grant = new GrantTabModel(); + // this.dmpModel.project = new ProjectFormModel(); + // this.dmpModel.funder = new FunderFormModel(); + // this.dmpModel.extraProperties = new ExtraPropertiesFormModel(); + // this.dmpModel.fromModel(data); + // this.dmpModel.status = DmpStatus.Draft; + // this.dmpFormGroup = this.dmpModel.buildForm(); - if (!isNullOrUndefined(this.dmpFormGroup.get('profile').value)) { - this.getBlueprintDefinition(Guid.parse(this.dmpFormGroup.get('profile').value), result => { - this.checkForGrant(result.definition); - this.checkForFunder(result.definition); - this.checkForProject(result.definition); - }); - } + // if (!isNullOrUndefined(this.dmpFormGroup.get('profile').value)) { + // this.getBlueprintDefinition(Guid.parse(this.dmpFormGroup.get('profile').value), result => { + // this.checkForGrant(result.definition); + // this.checkForFunder(result.definition); + // this.checkForProject(result.definition); + // }); + // } - if (!isNewVersion) { - this.dmpFormGroup.get('label').setValue(dmp.label + " New"); - } - this.openCloneDialog(isNewVersion); - }); + // if (!isNewVersion) { + // this.dmpFormGroup.get('label').setValue(dmp.label + " New"); + // } + // this.openCloneDialog(isNewVersion); + // }); } private getBlueprintDefinition(blueprintId: Guid, successFunction) { @@ -281,25 +270,25 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { cancelButton: this.language.instant('DMP-EDITOR.CLONE-DIALOG.CANCEL'), } }); - dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { - if (result) { - if (!isNewVersion) { - this.dmpService.clone(this.dmpFormGroup.getRawValue(), this.dmp.id) - .pipe(takeUntil(this._destroyed)) - .subscribe( - complete => this.onCloneOrNewVersionCallbackSuccess(complete), - error => this.onCloneOrNewVersionCallbackError(error) - ); - } else if (isNewVersion) { - this.dmpService.newVersion(this.dmpFormGroup.getRawValue(), this.dmp.id) - .pipe(takeUntil(this._destroyed)) - .subscribe( - complete => this.onCloneOrNewVersionCallbackSuccess(complete), - error => this.onCloneOrNewVersionCallbackError(error) - ); - } - } - }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // if (!isNewVersion) { + // this.dmpService.clone(this.dmpFormGroup.getRawValue(), this.dmp.id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // complete => this.onCloneOrNewVersionCallbackSuccess(complete), + // error => this.onCloneOrNewVersionCallbackError(error) + // ); + // } else if (isNewVersion) { + // this.dmpService.newVersion(this.dmpFormGroup.getRawValue(), this.dmp.id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // complete => this.onCloneOrNewVersionCallbackSuccess(complete), + // error => this.onCloneOrNewVersionCallbackError(error) + // ); + // } + // } + // }); } // newVersion(id: String, label: String) { @@ -307,53 +296,53 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { // window.open(url.toString(), '_blank'); // } - downloadXml(id: string) { - this.dmpService.downloadXML(id) - .pipe(takeUntil(this._destroyed)) - .subscribe(response => { - const blob = new Blob([response.body], { type: 'application/xml' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + downloadXml(id: Guid) { + // this.dmpService.downloadXML(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/xml' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); - FileSaver.saveAs(blob, filename); - this.matomoService.trackDownload('dmps', "xml", id); - }); + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('dmps', "xml", id); + // }); } - downloadDocx(id: string) { - this.dmpService.downloadDocx(id) - .pipe(takeUntil(this._destroyed)) - .subscribe(response => { - const blob = new Blob([response.body], { type: 'application/msword' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + downloadDocx(id: Guid) { + // this.dmpService.downloadDocx(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/msword' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); - FileSaver.saveAs(blob, filename); - this.matomoService.trackDownload('dmps', "docx", id); - }); + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('dmps', "docx", id); + // }); } - downloadPDF(id: string) { - this.dmpService.downloadPDF(id) - .pipe(takeUntil(this._destroyed)) - .subscribe(response => { - const blob = new Blob([response.body], { type: 'application/pdf' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + downloadPDF(id: Guid) { + // this.dmpService.downloadPDF(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/pdf' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); - FileSaver.saveAs(blob, filename); - this.matomoService.trackDownload('dmps', "pdf", id); - }); + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('dmps', "pdf", id); + // }); } - downloadJson(id: string) { - this.dmpService.downloadJson(id) - .pipe(takeUntil(this._destroyed)) - .subscribe(response => { - const blob = new Blob([response.body], { type: 'application/json' }); - const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); - FileSaver.saveAs(blob, filename); - this.matomoService.trackDownload('dmps', "json", id); - }, async error => { - this.onExportCallbackError(error); - }); + downloadJson(id: Guid) { + // this.dmpService.downloadJson(id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(response => { + // const blob = new Blob([response.body], { type: 'application/json' }); + // const filename = this.fileUtils.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + // FileSaver.saveAs(blob, filename); + // this.matomoService.trackDownload('dmps', "json", id); + // }, async error => { + // this.onExportCallbackError(error); + // }); } async onExportCallbackError(error: any) { @@ -406,7 +395,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { }); } - isDraftDmp(activity: DmpListingModel) { + isDraftDmp(activity: Dmp) { return activity.status == DmpStatus.Draft; } @@ -440,8 +429,8 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit { this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Error); } - isUserOwner(activity: DmpListingModel): boolean { - const principalId: string = this.authentication.userId()?.toString(); - if (principalId) return !!activity.users.find(x => (x.role === Role.Owner) && (principalId === x.id)); + isUserOwner(dmp: Dmp): boolean { + const principalId: Guid = this.authentication.userId(); + if (principalId) return !!dmp.dmpUsers.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id)); } } diff --git a/dmp-frontend/src/app/ui/language/language-content/language.component.html b/dmp-frontend/src/app/ui/language/language-content/language.component.html index d23cad05b..2515aebc1 100644 --- a/dmp-frontend/src/app/ui/language/language-content/language.component.html +++ b/dmp-frontend/src/app/ui/language/language-content/language.component.html @@ -1,5 +1,5 @@ - +
- {{lang.label | translate}} + {{'GENERAL.LANGUAGES.' + lang | translate}}
diff --git a/dmp-frontend/src/app/ui/language/language-content/language.component.ts b/dmp-frontend/src/app/ui/language/language-content/language.component.ts index 270b34c47..2bc6e76fc 100644 --- a/dmp-frontend/src/app/ui/language/language-content/language.component.ts +++ b/dmp-frontend/src/app/ui/language/language-content/language.component.ts @@ -6,6 +6,8 @@ import { UserServiceOld } from '@app/core/services/user/user.service-old'; import { takeUntil } from 'rxjs/operators'; import { BaseComponent } from '@common/base/base.component'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { MatButtonToggleChange } from '@angular/material/button-toggle'; +import { UserService } from '@app/core/services/user/user.service'; @Component({ selector: 'app-language', @@ -15,47 +17,49 @@ import { ConfigurationService } from '@app/core/services/configuration/configura export class LanguageComponent extends BaseComponent implements OnInit { @Output() languageChange: EventEmitter = new EventEmitter(); - languages = []; + languages: string[] = []; constructor( private router: Router, private authentication: AuthService, - private languageService: LanguageService, - private userService: UserServiceOld, - private configurationService:ConfigurationService + private userService: UserService, + private languageService: LanguageService ) { super(); - this.languages = this.configurationService.availableLanguages; + this.languages = this.languageService.getAvailableLanguagesCodes(); } ngOnInit() { - this.languageChange.emit(this.getCurrentLanguage().value) + this.languageChange.emit(this.getCurrentLanguage()) } public isAuthenticated(): boolean { return this.authentication.currentAccountIsAuthenticated(); } - public getCurrentLanguage(): any { - const lang = this.languages.find(lang => lang.value === this.languageService.getCurrentLanguage()); + public getCurrentLanguage(): string { + const lang = this.languages.find(lang => lang === this.languageService.getCurrentLanguage()); return lang; } - onLanguageSelected(selectedLanguage: any) { + onLanguageSelected(selectedLanguage: MatButtonToggleChange) { if (this.isAuthenticated()) { const langMap = new Map(); langMap.set('language', selectedLanguage.value); - this.userService.updateUserSettings({ language: this.languages.find(lang => lang.value === selectedLanguage.value) }) - .pipe(takeUntil(this._destroyed)) - .subscribe((response) => { - this.languageService.changeLanguage(selectedLanguage.value); - this.authentication.refresh() - .pipe(takeUntil(this._destroyed)) - .subscribe(innerResponse => { this.router.navigateByUrl(this.router.url); }); - }, - error => { - console.log(error); - }); + //TODO: refactor - save language selection to user profile + // this.userService.updateUserSettings({ language: this.languages.find(lang => lang === selectedLanguage.value) }) + // .pipe(takeUntil(this._destroyed)) + // .subscribe((response) => { + // this.languageService.changeLanguage(selectedLanguage.value); + // this.authentication.refresh() + // .pipe(takeUntil(this._destroyed)) + // .subscribe(innerResponse => { this.router.navigateByUrl(this.router.url); }); + // }, + // error => { + // console.log(error); + // }); + this.languageService.changeLanguage(selectedLanguage.value); + this.router.navigateByUrl(this.router.url); } else { this.languageService.changeLanguage(selectedLanguage.value); this.router.navigateByUrl(this.router.url); diff --git a/dmp-frontend/src/app/ui/navbar/navbar.component.ts b/dmp-frontend/src/app/ui/navbar/navbar.component.ts index b40bda80c..e274b8d19 100644 --- a/dmp-frontend/src/app/ui/navbar/navbar.component.ts +++ b/dmp-frontend/src/app/ui/navbar/navbar.component.ts @@ -54,8 +54,8 @@ export class NavbarComponent extends BaseComponent implements OnInit { super(); this.location = location; this.sidebarVisible = false; - this.languages = this.configurationService.availableLanguages; - this.selectedLanguage = this.configurationService.defaultLanguage || 'en'; + this.languages = this.languageService.getAvailableLanguagesCodes(); + this.selectedLanguage = this.languageService.getDefaultLanguagesCode(); } ngOnInit() { diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.html b/dmp-frontend/src/app/ui/sidebar/sidebar.component.html index c40d2d023..abd67f8e2 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.html +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.html @@ -4,32 +4,32 @@ diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts index 164399f41..802dd33a7 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts @@ -30,7 +30,7 @@ export const GENERAL_ROUTES: RouteInfo[] = [ ]; export const DMP_ROUTES: RouteInfo[] = [ { path: '/plans', title: 'SIDE-BAR.MY-DMPS', icon: 'library_books' }, - { path: '/datasets', title: 'SIDE-BAR.MY-DESCRIPTIONS', icon: 'dns' }, + { path: '/descriptions', title: 'SIDE-BAR.MY-DESCRIPTIONS', icon: 'dns' }, // { path: '/quick-wizard', title: 'SIDE-BAR.QUICK-WIZARD', icon: 'play_circle_outline' }, // { path: '/plans/new', title: 'SIDE-BAR.ADD-EXPERT', icon: 'playlist_add' } ]; @@ -51,18 +51,18 @@ export const PUBLIC_ROUTES: RouteInfo[] = [ export const ADMIN_ROUTES: RouteInfo[] = [ { path: '/dmp-blueprints', title: 'SIDE-BAR.DMP-BLUEPRINTS', icon: 'library_books' }, - { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'library_books' }, - { path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'library_books' }, - { path: '/references', title: 'SIDE-BAR.REFERENCES', icon: 'library_books' }, - { path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'library_books' }, - { path: '/tenants', title: 'SIDE-BAR.TENANTS', icon: 'library_books' }, + { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'description' }, + { path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'stack' }, + { path: '/references', title: 'SIDE-BAR.REFERENCES', icon: 'dataset_linked' }, + { path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'add_link' }, + { path: '/tenants', title: 'SIDE-BAR.TENANTS', icon: 'tenancy' }, { path: '/users', title: 'SIDE-BAR.USERS', icon: 'people' }, { path: '/languages', title: 'SIDE-BAR.LANGUAGES', icon: 'language' }, { path: '/supportive-material', title: 'SIDE-BAR.SUPPORTIVE-MATERIAL', icon: 'import_contacts' } ]; export const DATASET_TEMPLATE_ROUTES: RouteInfo[] = [ - { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'library_books' } + { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'description' } ]; export const INFO_ROUTES: RouteInfo[] = [ diff --git a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html index ad1a281e6..fc2b9ae43 100644 --- a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html +++ b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.html @@ -1,24 +1,24 @@
- -
-
- + +
+
+ {{'SUPPORTIVE-MATERIAL-EDITOR.FIELDS.MATERIAL-TYPE' | translate}} {{enumUtils.toSupportiveMaterialTypeString(type)}} - + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
-
- +
+ {{'SUPPORTIVE-MATERIAL-EDITOR.FIELDS.LANGUAGE' | translate}} - + {{languageCode}} @@ -28,9 +28,9 @@
- - - +
+ - +
+
-
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.ts b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.ts index ea32f19a2..6b6801e18 100644 --- a/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.ts +++ b/dmp-frontend/src/app/ui/supportive-material-editor/supportive-material-editor.component.ts @@ -30,7 +30,7 @@ import { SupportiveMaterialEditorResolver } from './supportive-material-editor.r import { IsActive } from '@app/core/common/enum/is-active.enum'; import { AppPermission } from '@app/core/common/enum/permission.enum'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { LanguageV2Service } from '@app/core/services/language/language-v2.service'; +import { LanguageHttpService } from '@app/core/services/language/language.http.service'; import { nameof } from 'ts-simple-nameof'; import { Language } from '@app/core/model/language/language'; import { LanguageLookup } from '@app/core/query/language.lookup'; @@ -69,7 +69,7 @@ export class SupportiveMaterialEditorComponent extends BaseEditor x.value === result['language']['value']).pop() : '', [Validators.required]), // timezone: new FormControl(result['timezone'], [Validators.required]), diff --git a/dmp-frontend/src/assets/config/config.json b/dmp-frontend/src/assets/config/config.json index 1ee43b27b..3bb2cfbb0 100644 --- a/dmp-frontend/src/assets/config/config.json +++ b/dmp-frontend/src/assets/config/config.json @@ -7,49 +7,6 @@ "Url": "localhost:5000/" }, "defaultCulture": "en-US", - "defaultLanguage": "en", - "availableLanguages": [ - { - "label": "GENERAL.LANGUAGES.ENGLISH", - "value": "en" - }, - { - "label": "GENERAL.LANGUAGES.GREEK", - "value": "gr" - }, - { - "label": "GENERAL.LANGUAGES.SPANISH", - "value": "es" - }, - { - "label": "GENERAL.LANGUAGES.GERMAN", - "value": "de" - }, - { - "label": "GENERAL.LANGUAGES.TURKISH", - "value": "tr" - }, - { - "label": "GENERAL.LANGUAGES.SLOVAK", - "value": "sk" - }, - { - "label": "GENERAL.LANGUAGES.SERBIAN", - "value": "sr" - }, - { - "label": "GENERAL.LANGUAGES.PORTUGUESE", - "value": "pt" - }, - { - "label": "GENERAL.LANGUAGES.CROATIAN", - "value": "hr" - }, - { - "label": "GENERAL.LANGUAGES.POLISH", - "value": "pl" - } - ], "defaultBlueprintId": "86635178-36a6-484f-9057-a934e4eeecd5", "keycloak": { "enabled": true, diff --git a/dmp-frontend/src/assets/i18n/de.json b/dmp-frontend/src/assets/i18n/de.json index f79b37740..850037eb1 100644 --- a/dmp-frontend/src/assets/i18n/de.json +++ b/dmp-frontend/src/assets/i18n/de.json @@ -182,16 +182,16 @@ "DOC": "Dokument" }, "LANGUAGES": { - "ENGLISH": "Englisch", - "GREEK": "Griechisch", - "SPANISH": "Spanisch", - "GERMAN": "German", - "TURKISH": "Turkish", - "SLOVAK": "Slovak", - "SERBIAN": "Serbian", - "PORTUGUESE": "Portuguese", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "Englisch", + "gr": "Griechisch", + "es": "Spanisch", + "de": "German", + "tr": "Turkish", + "sk": "Slovak", + "sr": "Serbian", + "pt": "Portuguese", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index f9fc44bb3..8d4a3b2fe 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -183,16 +183,16 @@ "DOC": "Document" }, "LANGUAGES": { - "ENGLISH": "English", - "GREEK": "Greek", - "SPANISH": "Spanish", - "GERMAN": "German", - "TURKISH": "Turkish", - "SLOVAK": "Slovak", - "SERBIAN": "Serbian", - "PORTUGUESE": "Portuguese", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "English", + "gr": "Greek", + "es": "Spanish", + "de": "German", + "tr": "Turkish", + "sk": "Slovak", + "sr": "Serbian", + "pt": "Portuguese", + "hr": "Croatian", + "pl": "Polish" } }, "HYBRID-LISTING": { diff --git a/dmp-frontend/src/assets/i18n/es.json b/dmp-frontend/src/assets/i18n/es.json index c02630386..b62f2a323 100644 --- a/dmp-frontend/src/assets/i18n/es.json +++ b/dmp-frontend/src/assets/i18n/es.json @@ -182,16 +182,16 @@ "DOC": "Documento" }, "LANGUAGES": { - "ENGLISH": "Inglés", - "GREEK": "Griego", - "SPANISH": "Español", - "GERMAN": "Alemán", - "TURKISH": "Turco", - "SLOVAK": "Eslovaco", - "SERBIAN": "Serbio", - "PORTUGUESE": "Portugués", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "Inglés", + "gr": "Griego", + "es": "Español", + "de": "Alemán", + "tr": "Turco", + "sk": "Eslovaco", + "sr": "Serbio", + "pt": "Portugués", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/gr.json b/dmp-frontend/src/assets/i18n/gr.json index db0c88bdb..e0dd83b04 100644 --- a/dmp-frontend/src/assets/i18n/gr.json +++ b/dmp-frontend/src/assets/i18n/gr.json @@ -182,16 +182,16 @@ "DOC": "Document" }, "LANGUAGES": { - "ENGLISH": "Αγγλικά", - "GREEK": "Ελληνικά", - "SPANISH": "Ισπανικά", - "GERMAN": "Γερμανικά", - "TURKISH": "Τούρκικα", - "SLOVAK": "Σλοβάκικα", - "SERBIAN": "Σερβικά", - "PORTUGUESE": "Πορτογαλικά", - "CROATIAN": "Κροατικά", - "POLISH": "Πολωνικά" + "en": "Αγγλικά", + "gr": "Ελληνικά", + "es": "Ισπανικά", + "de": "Γερμανικά", + "tr": "Τούρκικα", + "sk": "Σλοβάκικα", + "sr": "Σερβικά", + "pt": "Πορτογαλικά", + "hr": "Κροατικά", + "pl": "Πολωνικά" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/hr.json b/dmp-frontend/src/assets/i18n/hr.json index a43b550d7..56107e47d 100644 --- a/dmp-frontend/src/assets/i18n/hr.json +++ b/dmp-frontend/src/assets/i18n/hr.json @@ -182,16 +182,16 @@ "DOC": "DOCX" }, "LANGUAGES": { - "ENGLISH": "Engleski", - "GREEK": "Grčki", - "SPANISH": "Španjolski", - "GERMAN": "Njemački", - "TURKISH": "Turski", - "SLOVAK": "Slovački", - "SERBIAN": "Srpski", - "PORTUGUESE": "Portugalski", - "CROATIAN": "Hrvatski", - "POLISH": "Poljski" + "en": "Engleski", + "gr": "Grčki", + "es": "Španjolski", + "de": "Njemački", + "tr": "Turski", + "sk": "Slovački", + "sr": "Srpski", + "pt": "Portugalski", + "hr": "Hrvatski", + "pl": "Poljski" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/pl.json b/dmp-frontend/src/assets/i18n/pl.json index 505b92f5c..63f678ff1 100644 --- a/dmp-frontend/src/assets/i18n/pl.json +++ b/dmp-frontend/src/assets/i18n/pl.json @@ -182,16 +182,16 @@ "DOC": "Dokument" }, "LANGUAGES": { - "ENGLISH": "Angielski", - "GREEK": "Grecki", - "SPANISH": "Hiszpański", - "GERMAN": "Niemiecki", - "TURKISH": "Turecki", - "SLOVAK": "Słowacki", - "SERBIAN": "Serbski", - "PORTUGUESE": "Portugalski", - "CROATIAN": "Chorwacki", - "POLISH": "Polski" + "en": "Angielski", + "gr": "Grecki", + "es": "Hiszpański", + "de": "Niemiecki", + "tr": "Turecki", + "sk": "Słowacki", + "sr": "Serbski", + "pt": "Portugalski", + "hr": "Chorwacki", + "pl": "Polski" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/pt.json b/dmp-frontend/src/assets/i18n/pt.json index 258e3a372..94a985895 100644 --- a/dmp-frontend/src/assets/i18n/pt.json +++ b/dmp-frontend/src/assets/i18n/pt.json @@ -182,16 +182,16 @@ "DOC": "DOCX" }, "LANGUAGES": { - "ENGLISH": "Inglês", - "GREEK": "Grego", - "SPANISH": "Espanhol", - "GERMAN": "Alemão", - "TURKISH": "Turco", - "SLOVAK": "Eslovaco", - "SERBIAN": "Sérvio", - "PORTUGUESE": "Português", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "Inglês", + "gr": "Grego", + "es": "Espanhol", + "de": "Alemão", + "tr": "Turco", + "sk": "Eslovaco", + "sr": "Sérvio", + "pt": "Português", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/sk.json b/dmp-frontend/src/assets/i18n/sk.json index 008c50a08..596acd804 100644 --- a/dmp-frontend/src/assets/i18n/sk.json +++ b/dmp-frontend/src/assets/i18n/sk.json @@ -182,16 +182,16 @@ "DOC": "Dokument" }, "LANGUAGES": { - "ENGLISH": "Angličtina", - "GREEK": "Gréčtina", - "SPANISH": "Španielčtina", - "GERMAN": "Nemečina", - "TURKISH": "Turečtina", - "SLOVAK": "Slovenčina", - "SERBIAN": "Serbian", - "PORTUGUESE": "Portuguese", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "Angličtina", + "gr": "Gréčtina", + "es": "Španielčtina", + "de": "Nemečina", + "tr": "Turečtina", + "sk": "Slovenčina", + "sr": "Serbian", + "pt": "Portuguese", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/sr.json b/dmp-frontend/src/assets/i18n/sr.json index 538617693..4d83eabd0 100644 --- a/dmp-frontend/src/assets/i18n/sr.json +++ b/dmp-frontend/src/assets/i18n/sr.json @@ -182,16 +182,16 @@ "DOC": "Dokument" }, "LANGUAGES": { - "ENGLISH": "engleski", - "GREEK": "grčki", - "SPANISH": "španski", - "GERMAN": "nemački", - "TURKISH": "turski", - "SLOVAK": "slovački", - "SERBIAN": "Serbian", - "PORTUGUESE": "Portuguese", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "engleski", + "gr": "grčki", + "es": "španski", + "de": "nemački", + "tr": "turski", + "sk": "slovački", + "sr": "Serbian", + "pt": "Portuguese", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/assets/i18n/tr.json b/dmp-frontend/src/assets/i18n/tr.json index 2e71d78a1..15648b3c1 100644 --- a/dmp-frontend/src/assets/i18n/tr.json +++ b/dmp-frontend/src/assets/i18n/tr.json @@ -182,16 +182,16 @@ "DOC": "Belge" }, "LANGUAGES": { - "ENGLISH": "İngilizce", - "GREEK": "Yunanca", - "SPANISH": "İspanyolca", - "GERMAN": "Almanca", - "TURKISH": "Türkçe", - "SLOVAK": "Slovakça", - "SERBIAN": "Sırpça", - "PORTUGUESE": "Portekizce", - "CROATIAN": "Croatian", - "POLISH": "Polish" + "en": "İngilizce", + "gr": "Yunanca", + "es": "İspanyolca", + "de": "Almanca", + "tr": "Türkçe", + "sk": "Slovakça", + "sr": "Sırpça", + "pt": "Portekizce", + "hr": "Croatian", + "pl": "Polish" } }, "COOKIE": { diff --git a/dmp-frontend/src/index.html b/dmp-frontend/src/index.html index 0ebc83174..50b68e228 100644 --- a/dmp-frontend/src/index.html +++ b/dmp-frontend/src/index.html @@ -16,7 +16,7 @@ - +