diff --git a/src/app/_modules/administration/administration.module.ts b/src/app/_modules/administration/administration.module.ts index b38f845..aef8568 100644 --- a/src/app/_modules/administration/administration.module.ts +++ b/src/app/_modules/administration/administration.module.ts @@ -14,6 +14,10 @@ import {WidjetModule} from "@app/_modules/widjet/widjet.module"; import {AdministrateLicenceComponent} from "@app/_modules/administration/licence/administrate-licence.component"; import {AdministrateUsersComponent} from "@app/_modules/administration/users/administrate-users.component"; import {UsersModule} from "@app/_modules/users/users.module"; +import { ObjectTypeComponent } from './object-type/object-type.component'; +import { ObjectTypeListComponent } from '@app/_modules/administration/object-type/list/object-type-list.component'; +import { ObjectTypeListItemComponent } from '@app/_modules/administration/object-type/list/item/object-type-list-item.component'; +import {SortablejsModule} from "@dustfoundation/ngx-sortablejs"; type PathMatch = "full" | "prefix" | undefined; const routes = [ @@ -31,6 +35,7 @@ const routes = [ PagesModule, WidjetModule, UsersModule, + SortablejsModule, ], declarations: [ AdministrationPageComponent, @@ -38,7 +43,10 @@ const routes = [ AdministrateCommitteeComponent, AdministrateSitePagesComponent, AdministrateLicenceComponent, - AdministrateUsersComponent + AdministrateUsersComponent, + ObjectTypeComponent, + ObjectTypeListComponent, + ObjectTypeListItemComponent ], exports: [ RouterModule diff --git a/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.html b/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.html new file mode 100644 index 0000000..fb69a54 --- /dev/null +++ b/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.html @@ -0,0 +1,94 @@ +
+
+
+ +
+
+
+ + +
+ +
+ +
+
+

Группа объектов: {{group.name}}

+
+
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
titlenametyperequiredmultiplerelated +
+ +
+
{{field.title}}{{field.name}}{{field.type}}{{field.required}}{{field.multiple}}{{field.related}} +
+ +
+
+
+ +
+
+ +
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
diff --git a/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.scss b/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.ts b/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.ts new file mode 100644 index 0000000..9009184 --- /dev/null +++ b/src/app/_modules/administration/object-type/list/item/object-type-list-item.component.ts @@ -0,0 +1,186 @@ +import {Component, Input} from '@angular/core'; +import {concat, Subscription} from "rxjs"; +import {FormsService, ListsService} from "@app/_services"; +import {DialogService} from "@app/_services/dialog.service"; +import {ObjectTypesService} from "@app/_services/object-types.service"; +import {group} from "@angular/animations"; +import {concatAll} from "rxjs/operators"; + +@Component({ + selector: 'object-type-list-item', + templateUrl: './object-type-list-item.component.html', + styleUrls: ['./object-type-list-item.component.scss'] +}) +export class ObjectTypeListItemComponent { + @Input() objectType: any; + @Input() FieldsGroup: any; + @Input() parent: any; + public active = false; + public touched = false; + public subscription: Subscription; + + constructor( + public objectTypesService: ObjectTypesService, + private formsService: FormsService, + private listsService: ListsService, + private dialog: DialogService) { + } + + ngOnInit() { + this.subscription = this.listsService.controls(this.listId).subscribe(res => { + if (this.touched) this.fetch(); + }); + } + + ngOnDestroy() { + this.subscription?.unsubscribe(); + } + + + get listId() { + return this.objectType.id; + } + + get parentListId() { + return this.parent?.id || 'object-type-root'; + } + + get logo() { + return this.objectType?.logo?.data.links?.full; + } + + get noLogoLetters() { + return this.objectType.name.replace(' ', '').slice(0, 2); + } + + get children() { + return this.objectType.children?.data; + } + + get groups() { + return this.objectType.groups?.data; + } + + get fields() { + let allFields = []; + this.groups.forEach(function (groups){ + groups.fields.data.forEach(function (fields){ + allFields.push(fields); + }) + }); + return allFields; + } + + get isDeleted() { + return !!this.objectType.deletedAt; + } + get isHidden() { + return this.isDeleted && !this.showDeleted; + } + get showDeleted() { + return this.objectTypesService.showDeleted; + } + + get hasChildren() { + return this.showDeleted ? this.children?.length : this.children?.filter(objectType => {return !objectType.deletedAt}).length; + } + + + fetch() { + let include = ['children.children', 'groups']; + this.objectTypesService.show(this.objectType.id, {include: include.join(','), withTrashed: true}).subscribe(res => { + this.objectType = res.data; + }); + } + + clone() { + this.dialog.confirm(`Копировать страницу ${this.objectType.name}?`).subscribe( + resp=>{ + if (resp) { + this.dialog.waiting('Выполняется копирование данных. Подождите, пожалуйста') + this.objectTypesService.clone(this.objectType.id, {recursive: true}).subscribe(res => { + this.dialog.waiting(null); + this.listsService.refresh(this.parentListId); + this.refresh(); + }); + } + } + ) + } + + add() { + this.formsService.createModel('objectType', {extraProps: {parent: this.objectType.id}}, this.listId); + this.active = true; + this.refresh() + } + + edit() { + this.formsService.editModel('objectType', this.objectType.id, null, this.listId); + } + + delete() { + this.dialog.confirm(`Удалить страницу ${this.objectType.name}?`).subscribe( + resp=>{ + if (resp) this.objectTypesService.delete(this.objectType.id).subscribe(res => { + this.listsService.refresh(this.parentListId); + this.refresh(); + }); + } + ) + } + + restore() { + this.dialog.confirm(`Восстановить страницу ${this.objectType.name}?`).subscribe( + resp=>{ + if (resp) this.objectTypesService.restore(this.objectType.id, {}).subscribe(res => { + this.listsService.refresh(this.parentListId); + this.refresh(); + }); + } + ) + } + + private refresh(){ + this.objectTypesService.root({include:'children'}).subscribe( + res => { this.objectTypesService.rootObjectTypes = res } + ) + } + + toggle() { + this.active = !this.active; + } + + addGroup() { + this.formsService.createModel('fieldsGroup',{extraProps: {object_type: this.objectType.id}}); + this.active = true; + this.refresh() + } + + editGroup() { + this.formsService.editModel('fieldsGroup', this.objectType.groups.id, null, this.listId); + } + + deleteGroup() { + this.dialog.confirm(`Удалить страницу ${this.objectType.groups.name}?`).subscribe( + resp=>{ + if (resp) this.objectTypesService.delete(this.objectType.groups.id).subscribe(res => { + this.listsService.refresh(this.parentListId); + this.refresh(); + }); + } + ) + } + + addField() { + this.formsService.createModel('field'/*,{extraProps: {object_type: this.objectType.id}}*/); + this.refresh() + } + + editField() { + + } + + deleteField() { + + } +} diff --git a/src/app/_modules/administration/object-type/list/object-type-list.component.html b/src/app/_modules/administration/object-type/list/object-type-list.component.html new file mode 100644 index 0000000..08c291e --- /dev/null +++ b/src/app/_modules/administration/object-type/list/object-type-list.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/_modules/administration/object-type/list/object-type-list.component.scss b/src/app/_modules/administration/object-type/list/object-type-list.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/_modules/administration/object-type/list/object-type-list.component.ts b/src/app/_modules/administration/object-type/list/object-type-list.component.ts new file mode 100644 index 0000000..6e57827 --- /dev/null +++ b/src/app/_modules/administration/object-type/list/object-type-list.component.ts @@ -0,0 +1,86 @@ +import {Component, Input} from '@angular/core'; +import {Subscription} from "rxjs"; +import {SortableOptions} from "sortablejs"; +import {Router} from "@angular/router"; +import {ObjectTypesService} from "@app/_services/object-types.service"; +import {FormsService, ListsService} from "@app/_services"; + + +@Component({ + selector: 'object-type-list', + templateUrl: './object-type-list.component.html', + styleUrls: ['./object-type-list.component.scss'] +}) +export class ObjectTypeListComponent { + @Input() parent: any; + public objectTypes = []; + subscription: Subscription; + + public optionsObjectTypes: SortableOptions = { + group: 'object-types', + handle: '.logo', + onUpdate: (event: any) => {this.move(event)}, + onAdd: (event: any) => {this.move(event)} + }; + public optionsLocales: SortableOptions = { + group: 'site-locales', + handle: '.logo', + onUpdate: (event: any) => {this.move(event)}, + onAdd: (event: any) => {this.move(event)} + }; + + constructor(private router: Router, private objectTypesService: ObjectTypesService, private listsService: ListsService, private formsService: FormsService) { + } + + get listId() { + return this.parent?.id || 'object-types-root'; + } + + ngOnInit() { + this.subscription = this.listsService.controls(this.listId).subscribe(res => { + this.fetch(); + }); + } + + ngOnDestroy() { + this.subscription?.unsubscribe(); + } + + fetch() { + this.parent ? this.fetchSubObjectTypes() : this.fetchRootObjectTypes(); + } + + fetchRootObjectTypes() { + let include = [ + 'children.children', + 'groups', + 'groups.fields', + 'children.groups.fields' + ]; + this.objectTypesService.root({include: include.join(','), withTrashed: true}).subscribe(res => { + this.objectTypes = res.data; + }); + } + + fetchSubObjectTypes() { + let include = ['children.children', 'groups', 'children.groups.fields']; + this.objectTypesService.show(this.parent.id, {include: include.join(','), withTrashed: true}).subscribe(res => { + this.objectTypes = res.data?.children?.data; + }); + } + + + add() { + this.formsService.createModel('ObjectType', null, this.listId); + } + + + move(event: any) { + this.objectTypesService.move(event.item.id, {parent: this.parent?.id, ord: event.newIndex}).subscribe(res => { + this.listsService.refresh(this.parent?.id); + this.objectTypesService.root({include:'children'}).subscribe( + res => { this.objectTypesService.rootObjectTypes = res } + ) + }); + } +} diff --git a/src/app/_modules/administration/object-type/object-type.component.html b/src/app/_modules/administration/object-type/object-type.component.html new file mode 100644 index 0000000..70c4e37 --- /dev/null +++ b/src/app/_modules/administration/object-type/object-type.component.html @@ -0,0 +1,19 @@ + +
+
+ Показать скрытые + +
+
+
+ + Типовые объекты +
+ +
+
+ +
+ +

Лицензия не активна. Перейдите в раздел Данные о лицензии

+
diff --git a/src/app/_modules/administration/object-type/object-type.component.scss b/src/app/_modules/administration/object-type/object-type.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/_modules/administration/object-type/object-type.component.ts b/src/app/_modules/administration/object-type/object-type.component.ts new file mode 100644 index 0000000..6d4eb98 --- /dev/null +++ b/src/app/_modules/administration/object-type/object-type.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import {ObjectTypesService} from "@app/_services/object-types.service"; +import {FormsService} from "@app/_services"; +import {LicenceService} from "@app/_services/licence.service"; +import {DialogService} from "@app/_services/dialog.service"; + +@Component({ + selector: 'object-type', + templateUrl: './object-type.component.html', + styleUrls: ['./object-type.component.scss'] +}) +export class ObjectTypeComponent { + constructor(private objectTypesService: ObjectTypesService, private formsService: FormsService, public licenceService: LicenceService, private dialog: DialogService) { + } + + get isLicenceActive() { + return this.licenceService.isActive; + } + get isMultilang() { + return this.isLicenceActive && this.licenceService.hasOption('multilang'); + } + + + get showDeleted() { + return this.objectTypesService.showDeleted; + } + + ngOnInit() { + } + + ngOnDestroy() { + this.objectTypesService.showDeleted = false; + } + + addLocale() { + if (this.isMultilang) this.formsService.createModel('objectType', null, 'object-types-root'); + else this.dialog.alert('Данная опция не входит в Вашу лицензию. Для включения опции обратитесь в службу поддержки НИР (+7 499 490 04 65, help@nirgroup.ru)'); + } + + toggle() { + this.objectTypesService.showDeleted = !this.objectTypesService.showDeleted; + } +} diff --git a/src/app/_modules/administration/page/administration-page.component.html b/src/app/_modules/administration/page/administration-page.component.html index 3d4275a..fdbf1ff 100644 --- a/src/app/_modules/administration/page/administration-page.component.html +++ b/src/app/_modules/administration/page/administration-page.component.html @@ -6,7 +6,7 @@
-
+
@@ -14,6 +14,7 @@ +

Страница не найдена

diff --git a/src/app/_modules/administration/page/administration-page.component.ts b/src/app/_modules/administration/page/administration-page.component.ts index c529ea5..04feb44 100644 --- a/src/app/_modules/administration/page/administration-page.component.ts +++ b/src/app/_modules/administration/page/administration-page.component.ts @@ -36,9 +36,10 @@ export class AdministrationPageComponent { //this.tabs = [{name: 'company', title: 'Структура ФАУ «ФЦС»'}, {name: 'committee', title: 'Структура ТК 465'}]; //if (this.authService.isSuperAdmin) this.tabs.push({name: 'site-pages', title: 'Структура сайта'}); this.tabs = [ - {name: 'site-pages', title: 'Структура сайта', access: ["admin", "editor"]}, - {name: 'users', title: 'Пользователи', access: ["admin"]}, - {name: 'licence', title: 'Данные о лицензии', access: ["admin"]}]; + {name: 'site-pages', title: 'Структура сайта', access: ["admin", "editor"]}, + {name: 'users', title: 'Пользователи', access: ["admin"]}, + {name: 'licence', title: 'Данные о лицензии', access: ["admin"]}, + {name: 'object-type', title: 'Объекты сайта', access: ["admin"]}]; this.switchTab(this.route.snapshot.paramMap.get('tab')); } diff --git a/src/app/_services/fields-group.service.ts b/src/app/_services/fields-group.service.ts new file mode 100644 index 0000000..2dc2f34 --- /dev/null +++ b/src/app/_services/fields-group.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class FieldsGroupService { + + constructor() { } +} diff --git a/src/app/_services/object-types.service.ts b/src/app/_services/object-types.service.ts new file mode 100644 index 0000000..795f6ac --- /dev/null +++ b/src/app/_services/object-types.service.ts @@ -0,0 +1,108 @@ +import {Injectable} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; +import {environment} from '@environments/environment'; +import {Observable, BehaviorSubject} from "rxjs"; +import {LicenceService} from "@app/_services/licence.service"; +import { DialogService } from '@app/_services/dialog.service'; + + +@Injectable({providedIn: 'root'}) +export class ObjectTypesService { + public currentObjectTypesSubject = new BehaviorSubject(null); + public rootObjectTypesSubject = new BehaviorSubject(null); + public metaSubject = new BehaviorSubject({title: '', description: '', keywords: ''}); + public editModeSubject = new BehaviorSubject(false); + public showDeletedSubject = new BehaviorSubject(false); + public menuSelectedLink: string; + + public rootPage:any; + + constructor(private http: HttpClient, private licenceService: LicenceService, private dialog: DialogService) { + //this.find('/').subscribe(res => {this.rootPages = res.data}); + } + + get currentObjectType() { + return this.currentObjectTypesSubject.value; + } + set currentObjectType(val: any) { + this.currentObjectTypesSubject.next(val); + this.setMetaFromPage(val); + } + // get rootObjectTypes() { + // return this.rootObjectTypesSubject.value; + // } + set rootObjectTypes(val: any) { + this.rootObjectTypesSubject.next(val); + } + + // get isRtl() { + // return ['ar'].indexOf(this.rootPage?.slug) !== -1; + // } + + get editMode() { + return this.editModeSubject.value; + } + set editMode(value: boolean) { + if (value) { + let error = this.licenceService.checkEditAvailability(this.currentObjectType); + if (error) { + this.dialog.alert(error); + return; + } + } + this.editModeSubject.next(value); + } + + get showDeleted() { + return this.showDeletedSubject.value; + } + set showDeleted(value: boolean) { + this.showDeletedSubject.next(value); + } + + + root(params?: any): Observable { + return this.http.get(`${environment.apiUrl}/api/object-types`, {params: params}); + } + + find(url: string, params?: any): Observable { + if (!params) params = {}; + params.url = url; + return this.http.get(`${environment.apiUrl}/api/object-types/find`, {params: params}); + } + + list(params?: any): Observable { + return this.http.get(`${environment.apiUrl}/api/object-types`, {params: params}); + } + + show(id: string, params?: any): Observable { + return this.http.get(`${environment.apiUrl}/api/object-types/${id}`, {params: params}); + } + + delete(id: string): Observable { + return this.http.delete(`${environment.apiUrl}/api/object-types/${id}`); + } + + restore(id: string, data: any): Observable { + return this.http.patch(`${environment.apiUrl}/api/pages/restore/${id}`, data); + } + + deleteBackground(id: string): Observable { + return this.http.delete(`${environment.apiUrl}/api/pages/background/${id}`); + } + + move(id: string, data: any): Observable { + return this.http.put(`${environment.apiUrl}/api/pages/move/${id}`, data); + } + + clone(id: string, data: any): Observable { + return this.http.put(`${environment.apiUrl}/api/pages/clone/${id}`, data); + } + + setMetaFromPage(page: any) { + this.setMeta({title: page?.title || page?.name, description: page?.description || '', keywords: page?.keywords || ''}); + } + setMeta(meta: any) { + this.metaSubject.next(meta); + } +} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 1949cb4..94d94bc 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,8 +5,8 @@ export const environment = { production: false, apiUrl: 'http://api.nircms.lc', - clientId: 4, - clientSecret: 'KmGnhqVbEi3wlzkEyXi1JeNg9FtswdOdKQHpOcAu', + clientId: 2, + clientSecret: 'iyTS47vCZHgMxokKToa1HhOgfvFrwlOu7WkmJ3cQ', project: null, licence: 'POUFLO4YW7SU', defaultLocale: 'ru' diff --git a/src/environments/vniigaz-v2.env.dev.ts b/src/environments/vniigaz-v2.env.dev.ts index f9c9d46..8571930 100644 --- a/src/environments/vniigaz-v2.env.dev.ts +++ b/src/environments/vniigaz-v2.env.dev.ts @@ -5,8 +5,8 @@ export const environment = { production: false, apiUrl: 'http://api.nircms.lc', - clientId: 4, - clientSecret: 'KmGnhqVbEi3wlzkEyXi1JeNg9FtswdOdKQHpOcAu', + clientId: 2, + clientSecret: 'iyTS47vCZHgMxokKToa1HhOgfvFrwlOu7WkmJ3cQ', project: 'vniigaz-v2', licence: 'POUFLO4YW7SU', defaultLocale: 'ru' diff --git a/src/vniigaz-v2/component/layout/jumbotron/jumbotron.component.html b/src/vniigaz-v2/component/layout/jumbotron/jumbotron.component.html index 7499e5a..6f4b37f 100644 --- a/src/vniigaz-v2/component/layout/jumbotron/jumbotron.component.html +++ b/src/vniigaz-v2/component/layout/jumbotron/jumbotron.component.html @@ -10,14 +10,13 @@ Учреждено в 1998 -
Режим редактирования - -
+ +
diff --git a/src/vniigaz-v2/css/_admin.scss b/src/vniigaz-v2/css/_admin.scss index a637388..0049967 100644 --- a/src/vniigaz-v2/css/_admin.scss +++ b/src/vniigaz-v2/css/_admin.scss @@ -37,7 +37,7 @@ } .site-admin-company{ font-weight: 700; - } + } } ico.page-lable{ background-color: var(--prime); @@ -68,9 +68,9 @@ administrate-site-pages{ } } } - + ico.page-control{ - cursor: pointer; + cursor: pointer; svg{ color:var(--second); &:hover{ @@ -79,7 +79,7 @@ ico.page-control{ } } pages-tree{ - + .item:not(.home){ .bar{ padding-left: 50px; @@ -175,15 +175,130 @@ pages-tree{ } } } - - - - - - } +object-type-list{ + + .item:not(.home){ + .bar{ + padding-left: 50px; + + } + .item .bar{ + padding-left: 100px; + } + .item .item .bar{ + padding-left: 150px; + } + .item .item .item .bar{ + padding-left: 200px; + } + + .item .item .item .item .bar{ + padding-left: 250px; + } + + .item .item .item .item .item .bar{ + padding-left: 300px; + } + } + + + + .item { + flex-direction: column; + align-items: stretch; + .bar { + display: flex; + flex-direction: row; + align-items: flex-start; + padding: 16px 0; + border-bottom: #E0E0E0 solid 1px; + .left { + flex-shrink: 0; + width: 40px; + height: 24px; + cursor: pointer; + + } + .mid { + flex-grow: 1; + padding: 0 16px; + .info { + display: flex; + flex-direction: row; + align-items: center; + .logo { + display: flex; + align-items: center; + justify-content: center; + position: relative; + flex-shrink: 0; + width: 40px; + height: 40px; + margin-right: 16px; + border-radius: 100px; + background-color: var(--prime); + color: #ffffff; + cursor: move; + &:hover{ + background-color: var(--prime-act); + } + span{ + display: none; + } + } + .name { + p { + margin: 0; + a { + color: var(--grey-7); + } + &.sub { + font-size: 0.875rem; + color: #7f7f7f; + } + } + } + } + } + .right { + display: flex; + flex-direction: row; + align-items: flex-start; + flex-shrink: 1; + gap: 8px; + } + } + } + + + @media screen and (max-width: 1330px) { + .item { + .bar { + .mid { + padding: 0 12px; + .info .logo { + display: none; + } + } + .right { + display: none; + } + } + .items { + padding-left: 16px; + } + &.company { + .items { + padding-left: 40px; + } + } + } + } + +} administrate-licence{ font-family: PT Sans; @@ -193,7 +308,7 @@ administrate-licence{ line-height: 24px; color: var(--dark); .default{ - + .caption{ color: var(--second); } @@ -242,7 +357,7 @@ users-list{ flex-shrink: 0; } } - + users-list-item { display: flex; align-items: center; @@ -316,4 +431,4 @@ users-list{ } } -} \ No newline at end of file +}