diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/exception/GridException.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/exception/GridException.java new file mode 100644 index 00000000..0fca73c2 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/exception/GridException.java @@ -0,0 +1,14 @@ +package ru.micord.ervu.account_applications.component.exception; + +/** + * @author Adel Kalimullin + */ +public class GridException extends RuntimeException{ + public GridException(String message) { + super(message); + } + + public GridException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/field/persist/filter/StaticFilterComponent.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/field/persist/filter/StaticFilterComponent.java new file mode 100644 index 00000000..5d2b5775 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/field/persist/filter/StaticFilterComponent.java @@ -0,0 +1,13 @@ +package ru.micord.ervu.account_applications.component.field.persist.filter; + +import ru.cg.webbpm.modules.standard_annotations.validation.NotNull; +import ru.cg.webbpm.modules.webkit.beans.Behavior; + +/** + * @author Adel Kalimullin + */ + +public class StaticFilterComponent extends Behavior { + @NotNull + public String name; +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/model/LoadServiceResponse.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/LoadServiceResponse.java new file mode 100644 index 00000000..fff850f7 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/LoadServiceResponse.java @@ -0,0 +1,49 @@ +package ru.micord.ervu.account_applications.component.model; + + +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * @author Adel Kalimullin + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class LoadServiceResponse { + private Set data; + private int page; + private int perPage; + private int totalRows; + + public Set getData() { + return data; + } + + public void setData(Set data) { + this.data = data; + } + + public int getPage() { + return page; + } + + public void setPage(int page) { + this.page = page; + } + + public int getPerPage() { + return perPage; + } + + public void setPerPage(int perPage) { + this.perPage = perPage; + } + + public int getTotalRows() { + return totalRows; + } + + public void setTotalRows(int totalRows) { + this.totalRows = totalRows; + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/model/Role.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/Role.java new file mode 100644 index 00000000..5451c57b --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/Role.java @@ -0,0 +1,55 @@ +package ru.micord.ervu.account_applications.component.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +/** + * @author Adel Kalimullin + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class Role { + private String id; + private String name; + private String shortname; + private String displayName; + private String description; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getShortname() { + return shortname; + } + + public void setShortname(String shortname) { + this.shortname = shortname; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/model/User.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/User.java new file mode 100644 index 00000000..a5a7db65 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/model/User.java @@ -0,0 +1,93 @@ +package ru.micord.ervu.account_applications.component.model; + + + +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + + +/** + * @author Adel Kalimullin + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class User { + private String accountId; + private String accountFullName; + private String domainName; + private boolean enabled; + private boolean blocked; + private List roles; + + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public String getAccountFullName() { + return accountFullName; + } + + public void setAccountFullName(String accountFullName) { + this.accountFullName = accountFullName; + } + + public String getDomainName() { + return domainName; + } + + public void setDomainName(String domainName) { + this.domainName = domainName; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isBlocked() { + return blocked; + } + + public void setBlocked(boolean blocked) { + this.blocked = blocked; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + User user = (User) o; + return enabled == user.enabled && blocked == user.blocked && Objects.equals( + accountId, user.accountId) && Objects.equals(accountFullName, + user.accountFullName + ) && Objects.equals(domainName, user.domainName); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(accountId); + result = 31 * result + Objects.hashCode(accountFullName); + result = 31 * result + Objects.hashCode(domainName); + result = 31 * result + Boolean.hashCode(enabled); + result = 31 * result + Boolean.hashCode(blocked); + return result; + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/FilterType.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/FilterType.java new file mode 100644 index 00000000..c2f01d01 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/FilterType.java @@ -0,0 +1,11 @@ +package ru.micord.ervu.account_applications.component.property.grid; + +/** + * @author Adel Kalimullin + */ +public enum FilterType { + TEXT, + DATE, + NUMBER, + SET +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticColumn.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticColumn.java new file mode 100644 index 00000000..14a19eb1 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticColumn.java @@ -0,0 +1,10 @@ +package ru.micord.ervu.account_applications.component.property.grid; + +/** + * @author Adel Kalimullin + */ +public class StaticColumn { + public String column; + public String type; + public FilterType filterType; +} diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn.java new file mode 100644 index 00000000..7cfd277e --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn.java @@ -0,0 +1,88 @@ +package ru.micord.ervu.account_applications.component.property.grid; + +import java.util.Collection; +import java.util.Collections; + +import component.field.dataconvert.DataConverter; +import component.field.dataconvert.DataConverterProvider; +import component.field.persist.filter.AbstractFilterComponent; +import component.grid.model.PinnedType; +import property.grid.Column; +import property.grid.Formatter; +import utils.GridUtils; + +import ru.cg.webbpm.modules.database.bean.AggregationFunction; +import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumn; +import ru.cg.webbpm.modules.standard_annotations.editor.Visible; +import ru.cg.webbpm.modules.webkit.annotations.Exclude; +import ru.cg.webbpm.modules.webkit.annotations.Model; + +/** + * @author Adel Kalimullin + */ +@Model +public class StaticGridColumn extends AbstractFilterComponent + implements Column { + + public StaticColumn field; + public String displayName; + public String headerTooltip; + @Visible(predicate = "displayPopup != true") + public String columnTooltip; + public Integer width; + public boolean widthFixed; + public boolean autoHeight; + public boolean hidden; + public boolean sortable; + public boolean disableHiding; + public PinnedType pinned; + public boolean filter; + @Exclude + public Formatter formatter; + public AggregationFunction aggregationFunction; + @Exclude + public Formatter aggregationFormatter; + @Exclude + public Formatter exportFileFormatter; + public boolean displayPopup; + public boolean suppressHeaderMenu; + + public EntityColumn getField() { + return GridUtils.toEntityColumn(field.column); + } + + @Override + public String getDisplayName() { + return displayName; + } + + public Collection getEntityColumns() { + return Collections.singleton(getField()); + } + + public AggregationFunction getAggregationFunction() { + return this.aggregationFunction; + } + + public Formatter getFormatter() { + return this.formatter; + } + + public Formatter getAggregationFormatter() { + return this.aggregationFormatter; + } + + public Formatter getExportFormatter() { + return exportFileFormatter; + } + + public Object convertData(Object rawValue) { + if (this.dataConverter != null) { + return this.dataConverter.convertValueForSave(rawValue); + } + else { + DataConverter converter = DataConverterProvider.getDataConverter(rawValue.getClass()); + return converter.convertValueForSave(rawValue); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/service/ErvuUserGridLoadService.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/service/ErvuUserGridLoadService.java new file mode 100644 index 00000000..77898c34 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/service/ErvuUserGridLoadService.java @@ -0,0 +1,169 @@ +package ru.micord.ervu.account_applications.component.service; + +import java.lang.reflect.Field; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import model.FileModel; +import model.Filter; +import model.grid.GridDataExportFormat; +import model.grid.GridRow; +import model.grid.GridRows; +import model.grid.SortInfo; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriComponentsBuilder; +import ru.micord.ervu.account_applications.component.exception.GridException; +import ru.micord.ervu.account_applications.component.field.persist.filter.StaticFilterComponent; +import ru.micord.ervu.account_applications.component.model.LoadServiceResponse; +import ru.micord.ervu.account_applications.component.model.Role; +import ru.micord.ervu.account_applications.component.model.User; +import ru.micord.ervu.account_applications.security.context.SecurityContext; +import ru.micord.ervu.account_applications.util.StringUtils; +import service.GridService; + +import ru.cg.webbpm.modules.webkit.beans.Behavior; + +/** + * @author Adel Kalimullin + */ +@Service +public class ErvuUserGridLoadService extends Behavior implements GridService { + private static final String FILTER_PATH = "/service/idm/accounts/search/filter/v1"; + private final ObjectMapper objectMapper; + private final SecurityContext securityContext; + @Value("${ervu.http.timeout:10}") + private int httpTimeout; + @Value("${ervu.url:https://ervu-dev.pgs.rtlabs.ru}") + private String ervuUrl; + + public ErvuUserGridLoadService(ObjectMapper objectMapper, + SecurityContext securityContext) { + this.objectMapper = objectMapper; + this.securityContext = securityContext; + } + + @Override + public GridRows loadData(Integer offset, Integer limit, Filter[] filters, + SortInfo[] sortInfos) { + LoadServiceResponse loadServiceResponse = loadDataFromApi(offset, limit, filters); + List gridRows = convertToGridRows(loadServiceResponse.getData()); + return new GridRows(gridRows); + } + + @Override + public GridRows loadDataWithRowCount(Integer offset, Integer limit, Filter[] filters, + SortInfo[] sortInfos) { + LoadServiceResponse loadServiceResponse = loadDataFromApi(offset, limit, filters); + List gridRows = convertToGridRows(loadServiceResponse.getData()); + return new GridRows(gridRows, loadServiceResponse.getTotalRows()); + } + + @Override + public FileModel exportData(String[] columnIdsToExport, GridDataExportFormat exportFormat, + Filter[] filters, SortInfo[] sortInfo) { + return GridService.super.exportData(columnIdsToExport, exportFormat, filters, sortInfo); + } + + private LoadServiceResponse loadDataFromApi(Integer offset, Integer limit, + Filter[] filters) { + try { + Map requestBodyMap = createRequestBody(offset, limit, filters); + String requestBody = objectMapper.writeValueAsString(requestBodyMap); + + HttpClient httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(httpTimeout)) + .build(); + + HttpRequest request = HttpRequest.newBuilder() + .uri(UriComponentsBuilder.fromHttpUrl(ervuUrl) + .path(FILTER_PATH) + .build().toUri()) + .header(HttpHeaders.CONTENT_TYPE, "application/json") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + securityContext.getToken()) + .timeout(Duration.ofSeconds(httpTimeout)) + .POST(HttpRequest.BodyPublishers.ofString(requestBody)) + .build(); + + HttpResponse response = httpClient.send(request, + HttpResponse.BodyHandlers.ofString() + ); + return objectMapper.readValue(response.body(), + new TypeReference>() { + } + ); + } + catch (Exception e) { + throw new GridException("Ошибка при загрузке данных", e); + } + } + + private List convertToGridRows(Set users) { + List gridRows = new ArrayList<>(); + int rowIndex = 0; + + for (User user : users) { + GridRow gridRow = new GridRow(); + gridRow.put("row_uid", rowIndex++); + + for (Field field : User.class.getDeclaredFields()) { + processField(field, user, gridRow); + } + + gridRows.add(gridRow); + } + + return gridRows; + } + + private Map createRequestBody(Integer offset, Integer limit, Filter[] filters) { + int page = (offset / limit) + 1; + Map filterMap = new HashMap<>(); + filterMap.put("domainId", securityContext.getDomainId()); + + if (filters != null && filters.length > 0) { + filterMap.putAll(Arrays.stream(filters) + .filter(filter -> filter.getFilterModels() != null && filter.getFilterModels().length > 0) + .collect(Collectors.toMap( + filter -> getScriptInObject(filter.componentGuid, StaticFilterComponent.class).name, + filter -> filter.getFilterModels()[0].value, + (existing, replacement) -> replacement + )) + ); + } + + return Map.of( + "searchFilter", filterMap, + "page", page, + "perPage", limit + ); + } + + private void processField(Field field, User user, GridRow gridRow) { + field.setAccessible(true); + try { + Object value = field.get(user); + + if ("accountFullName".equals(field.getName()) && value instanceof String string) { + gridRow.putAll(StringUtils.splitFio(string)); + return; + } + if (value instanceof List list && !list.isEmpty() && list.get(0) instanceof Role) { + value = list.stream() + .map(role -> ((Role) role).getId()) + .collect(Collectors.joining(", ")); + } + gridRow.put(field.getName(), value != null ? value : ""); + } + catch (IllegalAccessException e) { + throw new GridException("Ошибка при получении значения поля: " + field.getName(), e); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContext.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContext.java index c15696e3..aee7cdb2 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContext.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContext.java @@ -8,6 +8,8 @@ public interface SecurityContext { String getUserId(); + String getToken(); + UserSession getUserSession(); } diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContextImpl.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContextImpl.java index 65a0557a..613fac16 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContextImpl.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/context/SecurityContextImpl.java @@ -21,6 +21,12 @@ public class SecurityContextImpl return auth != null ? auth.getUserSession().userId() : null; } + @Override + public String getToken() { + JwtTokenAuthentication auth = (JwtTokenAuthentication) SecurityContextHolder.getContext().getAuthentication(); + return auth != null ? (String) auth.getCredentials() : null; + } + @Override public UserSession getUserSession() { JwtTokenAuthentication auth = (JwtTokenAuthentication) SecurityContextHolder.getContext().getAuthentication(); diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/model/jwt/authentication/JwtTokenAuthentication.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/model/jwt/authentication/JwtTokenAuthentication.java index df940371..54283656 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/security/model/jwt/authentication/JwtTokenAuthentication.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/model/jwt/authentication/JwtTokenAuthentication.java @@ -9,9 +9,11 @@ import ru.micord.ervu.account_applications.security.model.jwt.UserSession; public class JwtTokenAuthentication implements Authentication { private final UserSession userSession; + private final String token; - public JwtTokenAuthentication(UserSession userSession) { + public JwtTokenAuthentication(UserSession userSession, String token) { this.userSession = userSession; + this.token = token; } public UserSession getUserSession() { @@ -25,7 +27,7 @@ public class JwtTokenAuthentication implements Authentication { @Override public Object getCredentials() { - return null; + return token; } @Override diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java index a15892f5..198cd59b 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java @@ -23,7 +23,7 @@ public class ErvuJwtAuthenticationProvider implements AuthenticationProvider { JwtTokenDummy jwtTokenDummy = (JwtTokenDummy) authentication; String jwtToken = jwtTokenDummy.getToken(); UserSession userSession = jwtTokenService.getUserSession(jwtToken); - return new JwtTokenAuthentication(userSession); + return new JwtTokenAuthentication(userSession, jwtToken); } @Override diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/util/StringUtils.java b/backend/src/main/java/ru/micord/ervu/account_applications/util/StringUtils.java new file mode 100644 index 00000000..8bb8c0fa --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/account_applications/util/StringUtils.java @@ -0,0 +1,22 @@ +package ru.micord.ervu.account_applications.util; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Adel Kalimullin + */ +public final class StringUtils { + + private StringUtils() { + } + + public static Map splitFio(String fio) { + String[] parts = fio.trim().split(" ", 3); + Map result = new HashMap<>(); + result.put("lastName", parts.length > 0 ? parts[0] : ""); + result.put("firstName", parts.length > 1 ? parts[1] : ""); + result.put("middleName", parts.length > 2 ? parts[2] : ""); + return result; + } +} diff --git a/frontend/src/resources/template/account_applications/component/grid/ErvuStaticGrid.html b/frontend/src/resources/template/account_applications/component/grid/ErvuStaticGrid.html new file mode 100644 index 00000000..8c375a2d --- /dev/null +++ b/frontend/src/resources/template/account_applications/component/grid/ErvuStaticGrid.html @@ -0,0 +1,54 @@ +
+ + + +
+ +
+
+ diff --git a/frontend/src/ts/account_applications/component/grid/CustomGridColumnFilterUtils.ts b/frontend/src/ts/account_applications/component/grid/CustomGridColumnFilterUtils.ts new file mode 100644 index 00000000..6baa5f4e --- /dev/null +++ b/frontend/src/ts/account_applications/component/grid/CustomGridColumnFilterUtils.ts @@ -0,0 +1,26 @@ +import { + FilterType +} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/FilterType"; +import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community"; +import {SetFilter} from "./SetFilter"; + +export class CustomGridColumnFilterUtils { + + public static columnFilter(type: FilterType) { + if (!type) { + return null; + } + + switch (type) { + case FilterType.NUMBER: + return NumberFilter; + case FilterType.DATE: + return DateFilter; + case FilterType.SET: + return SetFilter; + case FilterType.TEXT: + default: + return TextFilter; + } + } +} diff --git a/frontend/src/ts/account_applications/component/grid/ErvuStaticGrid.ts b/frontend/src/ts/account_applications/component/grid/ErvuStaticGrid.ts new file mode 100644 index 00000000..4c398175 --- /dev/null +++ b/frontend/src/ts/account_applications/component/grid/ErvuStaticGrid.ts @@ -0,0 +1,95 @@ +import {ChangeDetectionStrategy, Component} from "@angular/core"; +import {Event, GridRpcService, GridV2, GridV2Column, Visible} from "@webbpm/base-package"; +import { + StaticGridColumn +} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn"; +import { + ColDef, + FilterChangedEvent, + ICellRendererParams, + ITooltipParams, + ValueFormatterParams, + ValueGetterParams +} from "ag-grid-community"; +import {StaticColumnInitializer} from "./StaticColumnInitializer"; + +@Component({ + moduleId: module.id, + selector: 'ervu-static-grid', + templateUrl: './../../../../../src/resources/template/account_applications/component/grid/ErvuStaticGrid.html', + changeDetection: ChangeDetectionStrategy.OnPush + }) +export class ErvuStaticGrid extends GridV2 { + // todo: remove on updating platform version up to 3.188 + @Visible("false") + public columnFiltersChanged: Event = new Event(); + private rpcService: GridRpcService; + + protected initGrid() { + super.initGrid(); + this.rpcService = this.getScript(GridRpcService); + } + + getColumns(): any[] { + return this.getScriptsInChildren(GridV2Column) + .map(columnV2 => columnV2.getScript(StaticGridColumn)); + } + + protected columnV2ToColumnDef(column: GridV2Column): ColDef { + let gridColumn = column.getScript(StaticGridColumn); + let colDef = this.columnToColumnDef(gridColumn); + if (column.renderer) { + colDef.cellRenderer = (params: ICellRendererParams) => column.renderer.render(params); + } + if (column.valueGetter) { + colDef.valueGetter = (params: ValueGetterParams) => column.valueGetter.get(params); + } + if (column.tooltipValueGetter) { + if (gridColumn.columnTooltip) { + throw new Error( + "Only one type of tooltip should be specified: tooltipValueGetter or columnTooltip"); + } + colDef.tooltipValueGetter = (params: ITooltipParams) => column.tooltipValueGetter.get(params); + } + + column.setGridRef(this); + column.setColDef(colDef); + return colDef; + } + + protected columnToColumnDef(column: any): ColDef { + let colDef = StaticColumnInitializer.columnToColumnDef(this, column); + let columnComp = column.context; + colDef['columnUid'] = columnComp.getObjectId(); + + if (columnComp.cellCssClassRulesProvider) { + colDef.cellClassRules = columnComp.cellCssClassRulesProvider.getRules(); + } + + if (columnComp.valueFormatter) { + colDef.valueFormatter = (params?: ValueFormatterParams) => { + return columnComp.valueFormatter.format(params); + } + } + console.log("Создан ColDef:", colDef); + return colDef; + } + + @Visible() + public getRowDataSize(): number { + return this.rowData ? this.rowData.length : 0; + } + + // todo: remove on updating platform version up to 3.188 + @Visible() + public hasColumnFilters(): boolean { + const filterModel: { [key: string]: any; } = this.gridApi.getFilterModel(); + return !!filterModel && Object.keys(filterModel).length > 0; + } + + // todo: remove on updating platform version up to 3.188 + public columnFilterChanged(event: FilterChangedEvent) { + this.columnFiltersChanged.trigger(event); + super.columnFilterChanged(event); + } +} \ No newline at end of file diff --git a/frontend/src/ts/account_applications/component/grid/SetFilter.ts b/frontend/src/ts/account_applications/component/grid/SetFilter.ts new file mode 100644 index 00000000..946ff730 --- /dev/null +++ b/frontend/src/ts/account_applications/component/grid/SetFilter.ts @@ -0,0 +1,116 @@ +import {AgPromise, IDoesFilterPassParams, IFilterComp, IFilterParams} from "ag-grid-community"; + +export class SetFilter implements IFilterComp { + + private OPTION_TEMPLATE = ``; + + private eGui!: HTMLDivElement; + private selectAll: HTMLInputElement; + private checkboxes: HTMLInputElement[] = []; + private values: any[] = []; + private initialValues: any[] = []; + private filterActive: boolean; + private filterChangedCallback!: (additionalEventAttributes?: any) => void; + private filterParams!: IFilterParams; + private valueType: string; + + init(params: IFilterParams): void { + this.eGui = document.createElement('div'); + this.eGui.className = 'ag-set-filter'; + let index = 0; + this.selectAll = this.initCheckBox('selectAll', 'Все', index); + this.checkboxes.push(this.selectAll); + + params.api.getRenderedNodes() + .map(node => node.data[params.colDef.field]) + .sort((n1, n2) => n1 > n2 ? 1 : n1 < n2 ? -1 : 0) + .forEach(value => { + if (this.values.includes(value)) { + return; + } + index++; + let id = `option-${index}`; + let checkbox = this.initCheckBox(id, value, index); + this.checkboxes.push(checkbox); + this.values.push(value); + }); + this.initialValues = this.values.slice(); + this.filterParams = params; + this.filterActive = false; + this.filterChangedCallback = params.filterChangedCallback; + + if (this.values.length > 0) { + this.valueType = typeof this.values[0]; + } + }; + + private initCheckBox(id: string, value: string, index: number): HTMLInputElement { + this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE); + this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value; + let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement; + checkbox.setAttribute('id', id); + checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this)); + return checkbox; + } + + getGui(): HTMLDivElement { + return this.eGui; + }; + + onCheckBoxChanged(event: any) { + let checked = event.target.checked; + + if (event.target === this.selectAll) { + this.checkboxes.forEach(checkbox => checkbox.checked = checked); + this.values = checked ? this.initialValues.slice() : []; + } + else { + let value = event.target.nextElementSibling.textContent; + value = this.valueType === 'number' ? +value : value; + + if (checked) { + this.values.push(value); + + if (this.values.length == this.initialValues.length) { + this.selectAll.checked = true; + } + } + else { + let index = this.values.indexOf(value); + this.values.splice(index, 1); + this.selectAll.checked = false; + } + } + this.filterActive = !this.selectAll.checked; + this.filterChangedCallback(); + } + + doesFilterPass(params: IDoesFilterPassParams): boolean { + let { field } = this.filterParams.colDef; + return this.values.includes(params.data[field]); + } + + getModel(): any { + return this.isFilterActive() ? { value: this.values } : null; + } + + isFilterActive(): boolean { + return this.filterActive; + } + + setModel(model: any): void | AgPromise { + this.values = model == null ? this.initialValues.slice() : model.value; + let predicate = checkbox => model == null ? true : model.value.includes(checkbox.value); + this.checkboxes.filter(predicate) + .forEach(checkbox => checkbox.checked = true); + this.filterActive = !this.selectAll.checked; + this.filterChangedCallback(); + } + + destroy(): void { + this.checkboxes.forEach(checkBox => checkBox.removeEventListener('change', this.onCheckBoxChanged.bind(this))); + } +} diff --git a/frontend/src/ts/account_applications/component/grid/StaticColumnInitializer.ts b/frontend/src/ts/account_applications/component/grid/StaticColumnInitializer.ts new file mode 100644 index 00000000..ccf9fd04 --- /dev/null +++ b/frontend/src/ts/account_applications/component/grid/StaticColumnInitializer.ts @@ -0,0 +1,120 @@ +import { + DateTimeUtil, + DefaultTooltip, + GridCellTooltipUtils, + GridColumnComparatorUtils, + GridColumnKeyboardUtils, + GridValueFormatterUtils, + GridValueRendererUtils, + PinnedType, +} from "@webbpm/base-package"; +import { + ColDef, + DateFilter, + ICellRendererFunc, + SuppressKeyboardEventParams +} from "ag-grid-community"; +import { + StaticGridColumn +} from "../../../generated/ru/micord/ervu/account_applications/component/property/grid/StaticGridColumn"; +import {CustomGridColumnFilterUtils} from "./CustomGridColumnFilterUtils"; +import {Moment} from "moment"; +import * as moment from "moment-timezone"; + +export class StaticColumnInitializer { + + public static columnToColumnDef(gridRef: any, column: StaticGridColumn) { + const columnDef: ColDef = {}; + columnDef.headerName = column.displayName ? column.displayName : ''; + columnDef.headerClass = "custom-header"; + columnDef.width = column.width; + columnDef.suppressSizeToFit = column.widthFixed; + columnDef.hide = column.hidden; + columnDef.resizable = !column.widthFixed; + columnDef.headerComponentParams = {"disable_hiding": column.disableHiding || false}; + columnDef.lockVisible = column.disableHiding; + columnDef.headerTooltip = column.headerTooltip ? column.headerTooltip : column.displayName; + columnDef.suppressMenu = column.suppressHeaderMenu; + + if (column.pinned) { + columnDef.pinned = column.pinned == PinnedType.LEFT ? 'left' : 'right'; + } + columnDef['gridComp'] = this; + columnDef.sortable = column.sortable; + + if (column.sortable == null) { + columnDef.sortable = true; + } + + if (column.autoHeight) { + columnDef.autoHeight = column.autoHeight; + columnDef.cellClass = 'ag-grid-cell-wrap-text'; + } + + if (column.field) { + columnDef.field = column.field.column; + let type = column.field.type; + + if (type != null) { + if (gridRef.getRowModelType() == "clientSide") { + columnDef.comparator = GridColumnComparatorUtils.columnComparator(type); + } + columnDef.valueFormatter = GridValueFormatterUtils.columnFormatter(type); + columnDef.cellRenderer = gridRef.createRenderer(column); + } + + if (column.filter !== false) { + columnDef.floatingFilter = gridRef.floatingFilter; + columnDef.filter = CustomGridColumnFilterUtils.columnFilter(column.field.filterType); + + if (columnDef.filter === DateFilter) { + columnDef.filterParams = { + comparator: function (filterLocalDateAtMidnight, cellValue) { + + if (!cellValue) { + return -1; + } + + let filterMoment: Moment = moment.utc(filterLocalDateAtMidnight) + .add(-filterLocalDateAtMidnight.getTimezoneOffset(), 'm'); + let cellMoment: Moment = DateTimeUtil.parseToMidnightUTC(cellValue); + + if (filterMoment.isSame(cellMoment)) { + return 0; + } + + if (cellMoment.isBefore(filterMoment)) { + return -1; + } + + if (cellMoment.isAfter(filterMoment)) { + return 1; + } + }, + browserDatePicker: true, + }; + } + } + + columnDef.suppressKeyboardEvent = (params: SuppressKeyboardEventParams) => { + return GridColumnKeyboardUtils.suppressHomeAndEndKeyboardEvent(params); + } + } + + if (column.displayPopup) { + const renderer: ICellRendererFunc = columnDef.cellRenderer as ICellRendererFunc; + columnDef.cellRenderer = GridValueRendererUtils.tooltipValueRenderer(renderer); + } + else if (column.columnTooltip) { + columnDef.tooltipComponent = DefaultTooltip; + columnDef.tooltipValueGetter = GridCellTooltipUtils.fixedValueTooltip(column.columnTooltip); + } + else if (!gridRef.suppressColumnTooltip) { + columnDef.tooltipComponent = DefaultTooltip; + columnDef.tooltipValueGetter = GridCellTooltipUtils.defaultFormattedTooltip( + column.field ? column.field.type : null); + } + + return columnDef; + } +} diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index adbdaf33..a26ba2b0 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -24,6 +24,7 @@ import {TokenInterceptor} from "./interceptor/token.interceptor.service"; import {DropdownTreeViewComponent}from "../../account_applications/component/field/DropdownTreeViewComponent"; import {DropdownTreeviewSelectComponent} from "../../account_applications/component/external/ngx-treeview/dropdown-treeview-select/dropdown-treeview-select.component"; import {TreeviewModule} from "ngx-treeview"; +import {ErvuStaticGrid} from "../../account_applications/component/grid/ErvuStaticGrid"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -34,7 +35,8 @@ export const DIRECTIVES = [ forwardRef(() => VBoxLoadValues), forwardRef(() => DropdownTreeViewComponent), forwardRef(() => DropdownTreeviewSelectComponent), - forwardRef(() => ErvuAccountTextFieldGridEditor) + forwardRef(() => ErvuAccountTextFieldGridEditor), + forwardRef(() => ErvuStaticGrid) ]; @NgModule({ diff --git a/resources/src/main/resources/StaticColumn.component b/resources/src/main/resources/StaticColumn.component new file mode 100644 index 00000000..96658f94 --- /dev/null +++ b/resources/src/main/resources/StaticColumn.component @@ -0,0 +1,62 @@ + + + d60228dc-bcea-4942-b413-6816e2b568f1 + StaticColumn + false + + 3.186.1 + + + ru.cg.webbpm.packages.base.resources + 3.192.3 + + + + + StaticColumn + false + false + + + GridV2Column + component.grid + + true + true + + + + StaticGridColumn + ru.micord.ervu.account_applications.component.property.grid + + true + true + + + autoHeight + + true + + + + displayPopup + + true + + + + filter + + null + + + + sortable + + true + + + + + +