From 5cf6e2e310535edf3d31512a7eb23ee83a8324bb Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Mon, 20 Nov 2023 18:38:25 -0300 Subject: [PATCH 01/85] AMP-30574 - Sector Mappings API: endpoints --- .../ampapi/endpoints/gpi/ValidationUtils.java | 16 +- .../sectorMapping/SectorMappingEndpoints.java | 72 +++++++ .../sectorMapping/SectorMappingService.java | 110 ++++++++++ .../aim/dbentity/AmpSectorMapping.hbm.xml | 23 ++ .../module/aim/dbentity/AmpSectorMapping.java | 57 +++++ .../digijava/module/aim/util/SectorUtil.java | 199 +++++++++++++----- amp/repository/aim/module-config.xml | 42 ++-- 7 files changed, 443 insertions(+), 76 deletions(-) create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java create mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.hbm.xml create mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.java diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java index 78446f108d7..34b8d1a0a82 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java @@ -1,9 +1,6 @@ package org.digijava.kernel.ampapi.endpoints.gpi; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import java.util.*; import javax.validation.ConstraintViolation; import javax.validation.Validation; @@ -34,6 +31,17 @@ public static void requireValid(Object obj) { } } + public static void valuesValid(Collection possibleValues, Object value) { + for (Object possibleValue : possibleValues) { + if (possibleValue.equals(value)) { + return; + } + } + List violations = new ArrayList<>(); + violations.add("Invalid value: " + value); + throw new ApiRuntimeException(Response.Status.BAD_REQUEST, ApiError.toError(violations)); + } + private static List validate(T obj) { List errorMessages = new ArrayList(); Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java new file mode 100644 index 00000000000..7749b483cca --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java @@ -0,0 +1,72 @@ +package org.digijava.kernel.ampapi.endpoints.sectorMapping; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.digijava.kernel.ampapi.endpoints.gpi.ValidationUtils; +import org.digijava.kernel.ampapi.endpoints.security.AuthRule; +import org.digijava.kernel.ampapi.endpoints.util.ApiMethod; +import org.digijava.kernel.exception.DgException; +import org.digijava.module.aim.dbentity.AmpSectorMapping; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * @author Diego Rossi + */ +@Path("sectors-mapping") +@Api("sectors-mapping") +public class SectorMappingEndpoints { + + private final SectorMappingService smService = new SectorMappingService(); + + @GET + @Path("all-mappings") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(id = "getAllSectorMappings") + @ApiOperation("Returns all sector mappings.") + public Collection getAllSectorMappings() { + return smService.getAllSectorMappings(); + } + + @GET + @Path("sectors-classified/{classSector}") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(id = "getClassifiedSectors") + @ApiOperation("Returns primary or secondary sectors by parameter. 1: Primary, 2: Secondary") + public List getClassifiedSectors(@ApiParam("Property value") @PathParam("classSector") Long classSector) { + List paramValuesValid = Arrays.asList(1L, 2L); + ValidationUtils.valuesValid(paramValuesValid, classSector); + return smService.getClassifiedSectors(classSector); + } + + @GET + @Path("secondaries-by-primary/{primarySectorId}") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(id = "getSecondarySectorsByPrimary") + @ApiOperation("Returns a list of secondary sectors by primary sector id.") + public List getSecondarySectorsByPrimary(@ApiParam("Property value") @PathParam("primarySectorId") Long primarySectorId) { + return smService.getSecondSectorsByPrimary(primarySectorId); + } + + @POST + @Path("") + @ApiMethod(id = "createSectorMapping") //TODO: add -> authTypes = AuthRule.IN_ADMIN, + @ApiOperation("Create a sector mapping.") + public void createSectorMapping(AmpSectorMapping mapping) throws DgException { + smService.createSectorsMapping(mapping); + } + + @DELETE + @Path("/{idMapping}") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(id = "getClassifiedSectors") //TODO: add -> authTypes = AuthRule.IN_ADMIN, + @ApiOperation("Delete a sector mapping.") + public void deleteSectorMapping(@ApiParam("Property value") @PathParam("idMapping") Long idMapping) throws DgException { + smService.deleteSectorMapping(idMapping); + } +} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java new file mode 100644 index 00000000000..fbb554d8adf --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -0,0 +1,110 @@ +package org.digijava.kernel.ampapi.endpoints.sectorMapping; + +import org.apache.log4j.Logger; +import org.digijava.kernel.ampapi.endpoints.common.values.ValueConverter; +import org.digijava.kernel.exception.DgException; +import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.util.SectorUtil; + +import java.io.Serializable; +import java.util.*; + +/** + * @author Diego Rossi + */ +public class SectorMappingService { + + private static final Logger LOGGER = Logger.getLogger(ValueConverter.class); + private Map sectorClasses = new HashMap<>(); + private String getSectorClassById(Long id) { + if (sectorClasses.isEmpty()) { + sectorClasses.put(1L, AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME); + sectorClasses.put(2L, AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME); + } + return sectorClasses.get(id); + } + + public static class SingleSectorData implements Serializable { + private Long id; + private String value; + private Boolean isPrimary; + + SingleSectorData(Long id, String value, Boolean isPrimary) { + this.id = id; + this.value = value; + this.isPrimary = isPrimary; + } + + public Long getId() { + return id; + } + + public String getValue() { + return value; + } + + public Boolean getIsPrimary() { + return isPrimary; + } + } + + /** + * Returns a list of primary or secondary sectors by parameter. + */ + public List getClassifiedSectors(final Long classSector) { + List sectors = new ArrayList<>(); + + String sectorClass = getSectorClassById(classSector); + + if (sectorClass == null) { + LOGGER.error("Invalid sector class: " + classSector); + return sectors; + } + + List alClassConfig = SectorUtil.getAllClassificationConfigs(); + AmpClassificationConfiguration classConfig = findByName(alClassConfig, sectorClass); + + Long schemeId = classConfig.getClassification().getAmpSecSchemeId(); + Boolean isPrimary = classSector == 1L; + SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { + sectors.add(new SingleSectorData(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName(), isPrimary)); + }); + + return sectors; + } + + public Collection getAllSectorMappings() { + return SectorUtil.getAllSectorMappings(); + } + + public List getSecondSectorsByPrimary(Long idPrimary) { + List secondaries = new ArrayList<>(); + Collection mappings = SectorUtil.getSectorMappingsByPrimary(idPrimary); + + if (mappings != null) { + mappings.forEach(mapping -> { + AmpSectorMapping sectorMapping = (AmpSectorMapping) mapping; + AmpSector sector = sectorMapping.getDstSector(); + secondaries.add(new SingleSectorData(sector.getAmpSectorId(), sector.getName(), false)); + }); + } + return secondaries; + } + + public void createSectorsMapping(AmpSectorMapping mapping) throws DgException { + SectorUtil.createSectorMapping(mapping); + } + + public void deleteSectorMapping(Long id) throws DgException { + SectorUtil.deleteSectorMapping(id); + } + + private static AmpClassificationConfiguration findByName(List list, String name) { + for (AmpClassificationConfiguration item : list) { + if (item.getName().equals(name)) { + return item; + } + } + return null; + } +} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.hbm.xml b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.hbm.xml new file mode 100644 index 00000000000..d8f04d66f5f --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.hbm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + AMP_SECTOR_MAPPING_SEQ + + + + + + + + + diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.java new file mode 100644 index 00000000000..5c47d416613 --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpSectorMapping.java @@ -0,0 +1,57 @@ +package org.digijava.module.aim.dbentity; + +import com.fasterxml.jackson.annotation.*; + +/** + * @author Diego Rossi + */ +public class AmpSectorMapping { + + @JsonIgnore + private Long id; + + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "ampSectorId", + resolver = EntityResolver.class, scope = AmpSector.class) + @JsonIdentityReference(alwaysAsId = true) + @JsonProperty("src-sector") + private AmpSector srcSector; + + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "ampSectorId", + resolver = EntityResolver.class, scope = AmpSector.class) + @JsonIdentityReference(alwaysAsId = true) + @JsonProperty("dst-sector") + private AmpSector dstSector; + + public AmpSectorMapping() { + } + + public AmpSectorMapping(AmpSector srcSector, AmpSector dstSector) { + this.srcSector = srcSector; + this.dstSector = dstSector; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public AmpSector getSrcSector() { + return srcSector; + } + + public void setSrcSector(AmpSector srcSector) { + this.srcSector = srcSector; + } + + public AmpSector getDstSector() { + return dstSector; + } + + public void setDstSector(AmpSector dstSector) { + this.dstSector = dstSector; + } + +} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index 7cfa0b9c1c5..adb05686dd9 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -23,14 +23,7 @@ import org.dgfoundation.amp.ar.viewfetcher.SQLUtils; import org.digijava.kernel.exception.DgException; import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.module.aim.dbentity.AmpActivityGroup; -import org.digijava.module.aim.dbentity.AmpActivitySector; -import org.digijava.module.aim.dbentity.AmpActivityVersion; -import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; -import org.digijava.module.aim.dbentity.AmpIndicatorSector; -import org.digijava.module.aim.dbentity.AmpOrganisation; -import org.digijava.module.aim.dbentity.AmpSector; -import org.digijava.module.aim.dbentity.AmpSectorScheme; +import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.helper.ActivitySector; import org.digijava.module.aim.helper.Sector; import org.digijava.module.aim.util.caching.AmpCaching; @@ -43,7 +36,7 @@ /** * Utility class for persisting all Sector with Scheme related entities - * + * * @author Govind G Dalwani */ @@ -133,42 +126,42 @@ public static Collection searchForSector(String keyword, Long ampSecSchemeId) { } catch (Exception ex) { logger.debug("Unable to search sectors" + ex); - } + } return col; }// End Search Sector. public static List getAllSectorSchemes() { return getAllSectorSchemes(false); } - + /** * Get allSector Scheme - * @param classificationConfiguration check if the sector scheme has its associated ClassificationConfiguration + * @param classificationConfiguration check if the sector scheme has its associated ClassificationConfiguration * @return */ public static List getAllSectorSchemes(boolean classificationConfiguration) { try { String sectorSchemeNameHql = AmpSectorScheme.hqlStringForName("ss"); - + StringBuffer queryString =new StringBuffer( "select ss from " + AmpSectorScheme.class.getName() + " ss "); - if(classificationConfiguration){ + if(classificationConfiguration){ queryString.append("where exists ( from "+ AmpClassificationConfiguration.class.getName() +" cc where cc.classification.ampSecSchemeId=ss.ampSecSchemeId )"); } queryString.append(" order by " + sectorSchemeNameHql); - - + + Query qry = PersistenceManager.getSession().createQuery(queryString.toString()); List col = new ArrayList<>(qry.list()); return col; } catch (Exception ex) { throw new RuntimeException(ex); - } + } } - + @SuppressWarnings("unchecked") public static List getAllParentSectors(Long secSchemeId){ try - { + { String queryString = "select s from " + AmpSector.class.getName() + " s " + "where amp_sec_scheme_id = " + secSchemeId + " and parent_sector_id is null and (s.deleted is null or s.deleted = false) " + "order by " + AmpSector.hqlStringForName("s"); @@ -211,7 +204,7 @@ public static List getAllParentSectors() { public static Collection getAllSectors() { if (AmpCaching.getInstance().sectorsCache == null) { - Session session = null; + Session session = null; try { session = PersistenceManager.getRequestDBSession(); @@ -224,12 +217,12 @@ public static Collection getAllSectors() { } } return AmpCaching.getInstance().sectorsCache.getAllSectors(); - } - + } + public static Collection getAllChildSectors(Long parSecId) { if (AmpCaching.getInstance().sectorsCache == null) getAllSectors(); // force initialization of cache - + return AmpCaching.getInstance().sectorsCache.getChildSectors(parSecId); } @@ -519,8 +512,8 @@ public static Collection searchSectorName(String key) { } /** - * - * + * + * * @return List of sectors and sub-sectors ordered by sectors alphabetically * and then by sub-sectors alphabetically (Ex. A, a1, a2, a3, B, b1, * b2, etc...). The names of the sectors are a embelished (upper @@ -604,7 +597,7 @@ public static Collection getAmpSectorsAndSubSectorsHierarchyFast return new TreeSet<>(parents.values()); } - + /** * TODO: this is poor man's recursion * @param configurationName @@ -616,7 +609,7 @@ public static List getAmpSectorsAndSubSectorsHierarchy( getAllSectors(); //force rebuilding cache if (AmpCaching.getInstance().sectorsCache.sectorsHierarchy.containsKey(configurationName)) return new ArrayList(AmpCaching.getInstance().sectorsCache.sectorsHierarchy.get(configurationName)); - + List ret = new ArrayList(); Long id = null; @@ -655,7 +648,7 @@ public static List getAmpSectorsAndSubSectorsHierarchy( } } } - } + } AmpCaching.getInstance().sectorsCache.sectorsHierarchy.put(configurationName, ret); return new ArrayList(ret); } @@ -753,7 +746,7 @@ public static List getAllClassificationConfigsOr /** * Returns All Configurations of Classifications - * + * * @return All Configurations * @throws DgException * If exception occurred @@ -770,7 +763,7 @@ public static AmpClassificationConfiguration getClassificationConfigBySectorSche /** * Returns Classification Configuration by Configuration Id - * + * * @param configId * Configuration Id * @return Classification Configuration using Configuration Id @@ -800,7 +793,7 @@ public static AmpClassificationConfiguration getClassificationConfigById( /** * Returns true if specified Scheme is selected as default classification in * the configuration otherwise returns false. - * + * * @param classificationId * Id of classification * @return true If specified classification is selected as default @@ -841,7 +834,7 @@ public static boolean isSchemeUsed(Long classificationId) /** * Returns true if specified classification is selected as default * classification in the configuration otherwise returns false. - * + * * @param classificationId * Id of classification * @return true If specified classification is selected as default @@ -881,13 +874,13 @@ public static boolean isClassificationUsed(Long classificationId) /** * adds or update classification configuration - * - * + * + * * @param configId * Id of configuration * @param configName * Name of configuration - * @param description + * @param description * @param multiSector * @param classification * Default classification @@ -912,7 +905,7 @@ public static void saveClassificationConfig(Long configId, } config.setName(configName); - config.setDescription(description); + config.setDescription(description); config.setMultisector(multiSector); config.setClassification(classification); // beginTransaction(); @@ -953,7 +946,7 @@ public static int getClassificationConfigCount(String name, Long id) * Loads AmpClassificationConfiguration bean which is primary. Please see * next method which is old version and was not touch to not damage * anything. - * + * * @return primary configuration * @throws DgException */ @@ -980,8 +973,8 @@ public static AmpClassificationConfiguration getPrimaryConfigClassification() /** * gets id of classification which is selected in primary configuration - * - * + * + * * @return Id of classification * @throws DgException * If exception occurred @@ -1016,7 +1009,7 @@ public static Long getPrimaryConfigClassificationId() throws DgException { } /*** - * + * * @param classificationId */ public static void deleteClassification(Long classificationId) { @@ -1141,8 +1134,8 @@ public static Set populateWithDescendantsIds(Collection sectors { Set allOutputLocations = getRecursiveChildrenOfSectors(AlgoUtils.collectIds(new HashSet(), sectors)); return allOutputLocations; - } - + } + /** * recursively get all children of a set of AmpSectors, by a wave algorithm * @param inIds @@ -1150,10 +1143,10 @@ public static Set populateWithDescendantsIds(Collection sectors */ public static Set getRecursiveAscendantsOfSectors(Collection inIds) { - return AlgoUtils.runWave(inIds, + return AlgoUtils.runWave(inIds, new DatabaseWaver("SELECT DISTINCT (parent_sector_id) FROM amp_sector WHERE (parent_sector_id IS NOT NULL) AND (amp_sector_id IN ($))")); } - + /** * recursively get all children of a set of AmpCategoryValueLocations, by a wave algorithm * @param inIds @@ -1161,7 +1154,7 @@ public static Set getRecursiveAscendantsOfSectors(Collection inIds) */ public static Set getRecursiveChildrenOfSectors(Collection inIds) { - return AlgoUtils.runWave(inIds, + return AlgoUtils.runWave(inIds, new DatabaseWaver("SELECT DISTINCT amp_sector_id FROM amp_sector WHERE (deleted is null or deleted = false) AND parent_sector_id IN ($)")); } @@ -1172,10 +1165,10 @@ public static Set getRecursiveChildrenOfSectors(Collection inIds) */ public static Map> distributeSectorsByScheme(final Collection in) { final Map> ret = new HashMap<>(); - - if (in == null) + + if (in == null) return ret; - + PersistenceManager.getSession().doWork(new Work() { @Override public void execute(Connection connection) throws SQLException { @@ -1184,17 +1177,17 @@ public static Map> distributeSectorsByScheme(final Collection while (rs.rs.next()) { Long secId = rs.rs.getLong(1); String secScheme = rs.rs.getString(2); - + if (!ret.containsKey(secScheme)) ret.put(secScheme, new ArrayList()); ret.get(secScheme).add(secId); } } } - + }); return ret; } - + public static List getActivitiesForSector(Long id) { Session session = null; List activities = null; @@ -1214,7 +1207,7 @@ public static List getActivitiesForSector(Long id) { } return activities; } - + public static List getAllSectorColumnNames() { // not much benefit from generating names return Arrays.asList( @@ -1222,4 +1215,106 @@ public static List getAllSectorColumnNames() { ColumnConstants.SECONDARY_SECTOR, ColumnConstants.SECONDARY_SECTOR_SUB_SECTOR, ColumnConstants.SECONDARY_SECTOR_SUB_SUB_SECTOR, ColumnConstants.TERTIARY_SECTOR, ColumnConstants.TERTIARY_SECTOR_SUB_SECTOR, ColumnConstants.TERTIARY_SECTOR_SUB_SUB_SECTOR); } + + //region Sector Mapping + /** + * Returns all sector mappings + */ + public static Collection getAllSectorMappings() { + String queryString = null; + Session session = null; + Collection col = null; + Query qry = null; + + try { + session = PersistenceManager.getSession(); + queryString = "select asm from " + AmpSectorMapping.class.getName() + " asm"; + qry = session.createQuery(queryString); + col = qry.list(); + session.flush(); + } catch (Exception ex) { + logger.error("Unable to get sectors mappings from database " + ex.getMessage()); + ex.printStackTrace(System.out); + } + return col; + } + + public static Collection getSectorMappingsByPrimary(Long idPrimarySector) { + String queryString = null; + Session session = null; + Collection col = null; + Query qry = null; + + try { + session = PersistenceManager.getSession(); + queryString = "select asm from " + AmpSectorMapping.class.getName() + " asm " + + "where asm.srcSector.ampSectorId=:idPrimarySector"; + qry = session.createQuery(queryString); + qry.setParameter("idPrimarySector", idPrimarySector); + col = qry.list(); + session.flush(); + } catch (Exception ex) { + logger.error("Unable to get sectors mappings from database by primary " + ex.getMessage()); + ex.printStackTrace(System.out); + } + return col; + } + + /** + * adds an AmpSectorMapping + * + * @param sectorMapping + * @throws DgException + * If exception occurred + */ + public static void createSectorMapping(AmpSectorMapping sectorMapping) throws DgException { + Session session = null; + if (searchSectorMapping(sectorMapping.getSrcSector().getAmpSectorId(), sectorMapping.getDstSector().getAmpSectorId()) == null) { + try { + session = PersistenceManager.getRequestDBSession(); + session.saveOrUpdate(sectorMapping); + session.flush(); + } catch (Exception ex) { + logger.error("Unable to save a sector mapping " + ex.getMessage()); + throw new DgException(ex); + } + } + } + + public static void deleteSectorMapping(Long id) throws DgException { + Session session = null; + try { + session = PersistenceManager.getRequestDBSession(); + AmpSectorMapping asm = (AmpSectorMapping) session.load(AmpSectorMapping.class, id); + session.delete(asm); + session.flush(); + } catch (Exception ex) { + logger.error("Unable to delete a sector mapping " + ex.getMessage()); + throw new DgException(ex); + } + } + + private static AmpSectorMapping searchSectorMapping(Long pSrcSectorId, Long pDstSectorId) { + String queryString = null; + Session session = null; + Query qry = null; + AmpSectorMapping asm = null; + + try { + session = PersistenceManager.getSession(); + queryString = "select asm from " + AmpSectorMapping.class.getName() + + " asm where asm.srcSector.ampSectorId=:srcSectorId and asm.dstSector.ampSectorId=:dstSectorId"; + qry = session.createQuery(queryString); + qry.setParameter("srcSectorId", pSrcSectorId); + qry.setParameter("dstSectorId", pDstSectorId); + asm = (AmpSectorMapping)qry.uniqueResult(); + } catch (Exception ex) { + logger.error("Unable to get sectors mappings from database " + ex.getMessage()); + ex.printStackTrace(System.out); + } + return asm; + } + + + //endregion } diff --git a/amp/repository/aim/module-config.xml b/amp/repository/aim/module-config.xml index 1e353866729..c7bfcd335da 100644 --- a/amp/repository/aim/module-config.xml +++ b/amp/repository/aim/module-config.xml @@ -29,7 +29,7 @@ ADMIN ADMIN ADMIN - ADMIN + ADMIN ADMIN ADMIN ADMIN @@ -68,7 +68,7 @@ - + @@ -116,7 +116,7 @@ org.digijava.module.aim.dbentity.AmpReports org.digijava.module.aim.dbentity.AmpReportLog org.digijava.module.aim.dbentity.AmpSectorScheme - org.digijava.module.aim.dbentity.AmpTeamReports + org.digijava.module.aim.dbentity.AmpTeamReports org.digijava.module.aim.dbentity.AmpComponentType org.digijava.module.aim.dbentity.AmpComponent org.digijava.module.aim.dbentity.AmpComponentsIndicators @@ -140,7 +140,7 @@ org.digijava.module.aim.dbentity.AmpLineMinistryObservationMeasure org.digijava.module.aim.dbentity.AmpLineMinistryObservationActor org.digijava.module.aim.dbentity.AmpComponentFunding - org.digijava.module.aim.dbentity.AmpRegionalFunding + org.digijava.module.aim.dbentity.AmpRegionalFunding org.digijava.module.aim.dbentity.AmpReportsOptions org.digijava.module.aim.dbentity.AmpIndicatorRiskRatings org.digijava.module.aim.dbentity.AmpAhsurvey @@ -152,14 +152,14 @@ org.digijava.module.aim.dbentity.AmpGPISurveyIndicator org.digijava.module.aim.dbentity.AmpGPISurveyQuestion org.digijava.module.aim.dbentity.AmpGPISurveyResponse - org.digijava.module.aim.dbentity.AmpGPISurveyQuestionType + org.digijava.module.aim.dbentity.AmpGPISurveyQuestionType org.digijava.module.aim.dbentity.AmpGPINiAidOnBudget org.digijava.module.aim.dbentity.AmpGPINiIndicator org.digijava.module.aim.dbentity.AmpGPINiQuestion org.digijava.module.aim.dbentity.AmpGPINiQuestionOption org.digijava.module.aim.dbentity.AmpGPINiSurvey org.digijava.module.aim.dbentity.AmpGPINiSurveyResponse - org.digijava.module.aim.dbentity.AmpGPINiSurveyResponseDocument + org.digijava.module.aim.dbentity.AmpGPINiSurveyResponseDocument org.digijava.module.aim.dbentity.AmpGPINiDonorNotes org.digijava.module.aim.dbentity.AmpFeature org.digijava.module.aim.dbentity.AmpSiteFlag @@ -195,60 +195,60 @@ org.digijava.module.aim.dbentity.AmpChapter org.digijava.module.aim.dbentity.AmpImputation org.digijava.module.aim.dbentity.AmpColorThreshold - - org.digijava.module.aim.dbentity.AmpActivityGroup + + org.digijava.module.aim.dbentity.AmpActivityGroup org.digijava.module.aim.dbentity.AmpActivityVersion org.digijava.module.aim.dbentity.AmpAnnualProjectBudget org.digijava.module.aim.dbentity.AmpFundingAmount - + org.digijava.module.fundingpledges.dbentity.FundingPledges org.digijava.module.fundingpledges.dbentity.FundingPledgesLocation org.digijava.module.fundingpledges.dbentity.FundingPledgesProgram org.digijava.module.fundingpledges.dbentity.FundingPledgesSector - org.digijava.module.fundingpledges.dbentity.FundingPledgesDocument + org.digijava.module.fundingpledges.dbentity.FundingPledgesDocument org.digijava.module.fundingpledges.dbentity.FundingPledgesDetails - + org.digijava.module.aim.dbentity.AmpOrgLocation org.digijava.module.aim.dbentity.AmpOrgStaffInformation org.digijava.module.aim.dbentity.AmpOrganizationBudgetInformation org.digijava.module.aim.dbentity.AmpOrgRecipient - + org.digijava.module.budget.dbentity.AmpDepartments org.digijava.module.budget.dbentity.AmpBudgetSector org.digijava.module.aim.dbentity.AmpContact org.digijava.module.aim.dbentity.AmpActivityContact org.digijava.module.aim.dbentity.AmpContactProperty org.digijava.module.aim.dbentity.AmpOrganisationContact - + org.digijava.module.aim.dbentity.AmpStructureType org.digijava.module.aim.dbentity.AmpStructure org.digijava.module.aim.dbentity.AmpStructureImg - org.digijava.module.aim.dbentity.AmpStructureCoordinate + org.digijava.module.aim.dbentity.AmpStructureCoordinate org.digijava.module.aim.dbentity.OnepagerSection org.digijava.module.aim.dbentity.AmpContentTranslation - + org.digijava.module.aim.dbentity.GPISetup org.digijava.module.aim.dbentity.GPIDefaultFilters org.digijava.module.aim.dbentity.AmpAidEffectivenessIndicator org.digijava.module.aim.dbentity.AmpAidEffectivenessIndicatorOption - + org.digijava.module.aim.dbentity.AmpInterchangeableResult - + org.digijava.module.aim.dbentity.AmpMenuEntry - + org.digijava.module.aim.dbentity.AmpVisibilityRule - + org.digijava.module.aim.dbentity.AmpPerformanceRule org.digijava.module.aim.dbentity.AmpPerformanceRuleAttribute - + org.digijava.module.aim.dbentity.AmpFileType @@ -271,6 +271,8 @@ org.digijava.module.aim.dbentity.AmpActivityIndirectProgram org.digijava.module.aim.dbentity.AmpThemeMapping + org.digijava.module.aim.dbentity.AmpSectorMapping + From 620955bb894ac5bc15deba4da7a3b08c01626983 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Fri, 24 Nov 2023 18:46:33 -0300 Subject: [PATCH 02/85] AMP-30574 - Sector Mappings Improvements in Endpoints. Part of admin page --- amp/TEMPLATE/reampv2/src/App.route.jsx | 2 + .../actions/fetchAvailableSectors.js | 25 + .../sectorMapping/actions/fetchLayout.js | 22 + .../sectorMapping/actions/fetchSchemes.js | 25 + .../actions/fetchSectorMappings.js | 21 + .../sectorMapping/actions/fetchSettings.js | 22 + .../sectorMapping/actions/layoutAction.js | 44 + .../admin/sectorMapping/actions/saveAction.js | 23 + .../sectorMapping/actions/settingsAction.js | 22 + .../sectorMapping/actions/startupAction.js | 68 + .../sectorMapping/components/FormSectors.jsx | 295 + .../admin/sectorMapping/components/Header.jsx | 94 + .../admin/sectorMapping/components/Main.jsx | 111 + .../components/Notifications.jsx | 37 + .../SectorMappingAdminNavigator.jsx | 33 + .../components/SectorsHeader.jsx | 77 + .../admin/sectorMapping/components/Select.jsx | 70 + .../sectorMapping/components/Startup.jsx | 50 + .../components/common/BlockUI.css | 85 + .../components/common/BlockUI.jsx | 31 + .../components/common/HelpTooltip.jsx | 41 + .../components/css/Typeahead.css | 201 + .../components/css/bootstrap-4.4.1.css | 10382 ++++++++++++++++ .../css/images/icon-information.svg | 20 + .../sectorMapping/components/css/style.css | 228 + .../config/initialTranslations.json | 17 + .../sectorMapping/constants/Constants.jsx | 21 + .../src/modules/admin/sectorMapping/index.js | 38 + .../sectorMapping/reducers/rootReducer.js | 12 + .../reducers/saveSectorMappingReducer.js | 38 + .../sectorMapping/reducers/startupReducer.js | 68 + .../sector/config/initialTranslations.json | 11 + .../admin/sectorMapping/sector/index.js | 77 + .../sectorMapping/SectorMappingEndpoints.java | 33 +- .../sectorMapping/SectorMappingService.java | 109 +- .../dto/GenericSelectObjDTO.java | 23 + .../dto/MappingConfigurationDTO.java | 31 + .../dto/SchemaClassificationDTO.java | 15 + .../digijava/module/aim/util/SectorUtil.java | 31 + 39 files changed, 12487 insertions(+), 66 deletions(-) create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchAvailableSectors.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchLayout.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSchemes.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSectorMappings.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSettings.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/settingsAction.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Notifications.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.css create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/Typeahead.css create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/bootstrap-4.4.1.css create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/images/icon-information.svg create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/style.css create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/SchemaClassificationDTO.java diff --git a/amp/TEMPLATE/reampv2/src/App.route.jsx b/amp/TEMPLATE/reampv2/src/App.route.jsx index d7b656ee3fe..4a9a42f6b12 100644 --- a/amp/TEMPLATE/reampv2/src/App.route.jsx +++ b/amp/TEMPLATE/reampv2/src/App.route.jsx @@ -9,6 +9,7 @@ const AdminNDDApp = lazy(() => import('./modules/admin/ndd')); const NDDDashboardApp = lazy(() => import('./modules/ndddashboard')); const ReportGeneratorApp = lazy(() => import('./modules/report_generator')); const GeocoderApp = lazy(() => import('./modules/geocoder')); +const AdminSectorMappingApp = lazy(() => import('./modules/admin/sectorMapping')); class AppRoute extends Component { render() { @@ -21,6 +22,7 @@ class AppRoute extends Component { + ); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchAvailableSectors.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchAvailableSectors.js new file mode 100644 index 00000000000..b043b82c525 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchAvailableSectors.js @@ -0,0 +1,25 @@ +import { + fetchSectorsSuccess, + fetchSectorsPending, + fetchSectorsError +} from './startupAction'; + +function fetchSectors(url, type) { + return dispatch => { + dispatch(fetchSectorsPending()); + fetch(url + '/' + type) + .then(res => res.json()) + .then(res => { + if (res.error) { + throw (res.error); + } + dispatch(fetchSectorsSuccess(res)); + return res; + }) + .catch(error => { + dispatch(fetchSectorsError(error)); + }); + }; +} + +export default fetchSectors; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchLayout.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchLayout.js new file mode 100644 index 00000000000..ec693559e6f --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchLayout.js @@ -0,0 +1,22 @@ +import { fetchLayoutSuccess, fetchLayoutPending, fetchLayoutError } from './layoutAction'; +import { LAYOUT_EP } from '../constants/Constants'; + +function fetchLayout() { + return dispatch => { + dispatch(fetchLayoutPending()); + return fetch(LAYOUT_EP) + .then(res => res.json()) + .then(res => { + if (res.error) { + throw (res.error); + } + dispatch(fetchLayoutSuccess(res)); + return res; + }) + .catch(error => { + dispatch(fetchLayoutError(error)); + }); + }; +} + +export default fetchLayout; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSchemes.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSchemes.js new file mode 100644 index 00000000000..26fc4e6d8df --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSchemes.js @@ -0,0 +1,25 @@ +import { + fetchSchemesSuccess, + fetchSchemesPending, + fetchSchemesError +} from './startupAction'; + +function fetchSchemes(url) { + return dispatch => { + dispatch(fetchSchemesPending()); + fetch(url) + .then(res => res.json()) + .then(res => { + if (res.error) { + throw (res.error); + } + dispatch(fetchSchemesSuccess(res)); + return res; + }) + .catch(error => { + dispatch(fetchSchemesError(error)); + }); + }; +} + +export default fetchSchemes; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSectorMappings.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSectorMappings.js new file mode 100644 index 00000000000..28873360fdf --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSectorMappings.js @@ -0,0 +1,21 @@ +import { fetchSectorMappingSuccess, fetchSectorMappingPending, fetchSectorMappingError } from './startupAction'; + +function fetchSectorMappings(url) { + return dispatch => { + dispatch(fetchSectorMappingPending()); + fetch(url) + .then(res => res.json()) + .then(res => { + if (res.error) { + throw (res.error); + } + dispatch(fetchSectorMappingSuccess(res)); + return res; + }) + .catch(error => { + dispatch(fetchSectorMappingError(error)); + }); + }; +} + +export default fetchSectorMappings; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSettings.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSettings.js new file mode 100644 index 00000000000..dfb362ca8af --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/fetchSettings.js @@ -0,0 +1,22 @@ +import { fetchSettingsSuccess, fetchSettingsPending, fetchSettingsError } from './settingsAction'; +import { SETTINGS_EP } from '../constants/Constants'; + +function fetchSettings() { + return dispatch => { + dispatch(fetchSettingsPending()); + return fetch(SETTINGS_EP) + .then(res => res.json()) + .then(res => { + if (res.error) { + throw (res.error); + } + dispatch(fetchSettingsSuccess(res)); + return res; + }) + .catch(error => { + dispatch(fetchSettingsError(error)); + }); + }; +} + +export default fetchSettings; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js new file mode 100644 index 00000000000..0a2cf18ac0f --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js @@ -0,0 +1,44 @@ +export const FETCH_LAYOUT_PENDING = 'FETCH_LAYOUT_PENDING'; +export const FETCH_LAYOUT_SUCCESS = 'FETCH_LAYOUT_SUCCESS'; +export const FETCH_LAYOUT_ERROR = 'FETCH_LAYOUT_ERROR'; +export const FETCH_SECTORS_PENDING = 'FETCH_SECTORS_PENDING'; +export const FETCH_SECTORS_SUCCESS = 'FETCH_SECTORS_SUCCESS'; +export const FETCH_SECTORS_ERROR = 'FETCH_SECTORS_ERROR'; + +export function fetchLayoutPending() { + return { + type: FETCH_LAYOUT_PENDING + }; +} + +export function fetchLayoutSuccess(layout) { + return { + type: FETCH_LAYOUT_SUCCESS, + payload: layout + }; +} +export function fetchLayoutError(error) { + return { + type: FETCH_LAYOUT_ERROR, + error + }; +} + +export function fetchSectorsPending() { + return { + type: FETCH_SECTORS_PENDING + }; +} + +export function fetchSectorsSuccess(sectors) { + return { + type: FETCH_SECTORS_SUCCESS, + payload: sectors + }; +} +export function fetchSectorsError(error) { + return { + type: FETCH_SECTORS_ERROR, + error + }; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js new file mode 100644 index 00000000000..25255659811 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js @@ -0,0 +1,23 @@ +export const SAVE_SECTOR_MAP_PENDING = 'SAVE_SECTOR_MAP_PENDING'; +export const SAVE_SECTOR_MAP_SUCCESS = 'SAVE_SECTOR_MAP_SUCCESS'; +export const SAVE_SECTOR_MAP_ERROR = 'SAVE_SECTOR_MAP_ERROR'; + +export function saveSectorMappingPending() { + return { + type: SAVE_SECTOR_MAP_PENDING + }; +} + +export function saveSectorMappingSuccess(ndd) { + return { + type: SAVE_SECTOR_MAP_SUCCESS, + payload: ndd + }; +} + +export function saveSectorMappingError(error) { + return { + type: SAVE_SECTOR_MAP_ERROR, + error + }; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/settingsAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/settingsAction.js new file mode 100644 index 00000000000..3bf8a7e7e63 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/settingsAction.js @@ -0,0 +1,22 @@ +export const FETCH_SETTINGS_PENDING = 'FETCH_SETTINGS_PENDING'; +export const FETCH_SETTINGS_SUCCESS = 'FETCH_SETTINGS_SUCCESS'; +export const FETCH_SETTINGS_ERROR = 'FETCH_SETTINGS_ERROR'; + +export function fetchSettingsPending() { + return { + type: FETCH_SETTINGS_PENDING + }; +} + +export function fetchSettingsSuccess(settings) { + return { + type: FETCH_SETTINGS_SUCCESS, + payload: settings + }; +} +export function fetchSettingsError(error) { + return { + type: FETCH_SETTINGS_ERROR, + error + }; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js new file mode 100644 index 00000000000..37481655b34 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js @@ -0,0 +1,68 @@ +export const FETCH_SECTOR_MAP_PENDING = 'FETCH_SECTOR_MAP_PENDING'; +export const FETCH_SECTOR_MAP_SUCCESS = 'FETCH_SECTOR_MAP_SUCCESS'; +export const FETCH_SECTOR_MAP_ERROR = 'FETCH_SECTOR_MAP_ERROR'; +export const FETCH_SECTORS_PENDING = 'FETCH_SECTORS_PENDING'; +export const FETCH_SECTORS_SUCCESS = 'FETCH_SECTORS_SUCCESS'; +export const FETCH_SECTORS_ERROR = 'FETCH_SECTORS_ERROR'; + +export const FETCH_SCHEMES_PENDING = 'FETCH_SCHEMES_PENDING'; +export const FETCH_SCHEMES_SUCCESS = 'FETCH_SCHEMES_SUCCESS'; +export const FETCH_SCHEMES_ERROR = 'FETCH_SCHEMES_ERROR'; + +export function fetchSectorMappingPending() { + return { + type: FETCH_SECTOR_MAP_PENDING + }; +} + +export function fetchSectorMappingSuccess(sectorMapping) { + return { + type: FETCH_SECTOR_MAP_SUCCESS, + payload: sectorMapping + }; +} +export function fetchSectorMappingError(error) { + return { + type: FETCH_SECTOR_MAP_ERROR, + error + }; +} +export function fetchSectorsPending() { + return { + type: FETCH_SECTORS_PENDING + }; +} + +export function fetchSectorsSuccess(sectors) { + return { + type: FETCH_SECTORS_SUCCESS, + payload: sectors + }; +} +export function fetchSectorsError(error) { + return { + type: FETCH_SECTORS_ERROR, + error + }; +} + + +export function fetchSchemesPending() { + return { + type: FETCH_SCHEMES_PENDING + }; +} + +export function fetchSchemesSuccess(schemes) { + return { + type: FETCH_SCHEMES_SUCCESS, + payload: schemes + }; +} +export function fetchSchemesError(error) { + return { + type: FETCH_SCHEMES_ERROR, + error + }; +} + diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx new file mode 100644 index 00000000000..0d368a9a4f8 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx @@ -0,0 +1,295 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import { SectorMappingContext } from './Startup'; +import './css/style.css'; +//import * as Utils from '../utils/Utils'; +import { + DST_SCHEME_SECTOR, + SRC_SCHEME_SECTOR, + SECTOR_MAPPING, + SRC_SECTOR, + DST_SECTOR, + TYPE_SRC, + TYPE_DST, + ALL_SCHEMES +} from '../constants/Constants'; + +import {sendNDDError, sendNDDPending, sendNDDSaving} from "../../ndd/reducers/saveNDDReducer"; +import {updateActivitiesError, updateActivitiesPending} from "../../ndd/reducers/updateActivitiesReducer"; +import saveNDD from "../../ndd/actions/saveNDD"; +import updateActivities from "../../ndd/actions/updateActivities"; +import SectorsHeader from "./SectorsHeader"; +import Notifications from "./Notifications"; + +class FormSectors extends Component { + + constructor(props) { + super(props); + this.state = { + data: [], + validationErrors: undefined, + src: undefined, + dst: undefined, + schemes: undefined, + updatedActivities: false, + blockUI: false, + unsavedChanges: false, + saved: false, + level: 0 + }; + this.addRow = this.addRow.bind(this); + this.saveAll = this.saveAll.bind(this); + this.onRowChange = this.onRowChange.bind(this); + this.remove = this.remove.bind(this); + this.clearMessages = this.clearMessages.bind(this); + // this.onChangeMainProgram = this.onChangeMainProgram.bind(this); + this.onChangeMainScheme = this.onChangeMainScheme.bind(this); + this.clearAll = this.clearAll.bind(this); + } + + componentDidMount() { + const { mappings, schemes, translations, trnPrefix, settings, isIndirect } = this.context; + document.title = translations[`${trnPrefix}page-title`]; + + // Load Source Scheme Sector selected + this.setState(() => { + console.log('mappings: ', mappings); + if (mappings[SRC_SCHEME_SECTOR]) { + const src = {id: mappings[SRC_SCHEME_SECTOR].id, value: mappings[SRC_SCHEME_SECTOR].value}; + return {src}; + } + return { src: undefined }; + }); + + // Load Destination Scheme Sector selected + this.setState(() => { + if (mappings[DST_SCHEME_SECTOR]) { + const dst = {id: mappings[DST_SCHEME_SECTOR].id, value: mappings[DST_SCHEME_SECTOR].value}; + return {dst}; + } + return { dst: undefined }; + }); + + this.setState(() => ({ schemes })); + + // Load Sector Mappings saved + this.setState(previousState => { + const data = [...previousState.data]; + if (mappings[SECTOR_MAPPING]) { + mappings[SECTOR_MAPPING].forEach(mapping => { + const pair = {id: `${mapping[SRC_SECTOR]}${mapping[DST_SECTOR]}` }; + pair[SRC_SECTOR] = { + id: mapping[SRC_SECTOR], + value: this.getSectorNameFromScheme(mappings, mapping[SRC_SECTOR], TYPE_SRC) + }; + + pair[DST_SECTOR] = { + id: mapping[DST_SECTOR], + value: this.getSectorNameFromScheme(mappings, mapping[DST_SECTOR], TYPE_DST) + }; + + data.push(pair); + }); + } + return { data }; + }); + } + + getSectorNameFromScheme(mappings, sectorId, type) { + const key = type === TYPE_SRC ? SRC_SCHEME_SECTOR : DST_SCHEME_SECTOR; + + if (mappings) { + const schemeId = mappings[key].id; + const scheme = mappings[ALL_SCHEMES].find(s => s.id === schemeId); + if (scheme) { + const sector = scheme.children.find(s => s.id === sectorId); + if (sector) { + return sector.value; + } + } + } + return ''; + } + + componentDidUpdate(prevProps) { + if (prevProps !== this.props) { + // eslint-disable-next-line react/no-did-update-set-state + this.setState(previousState => { + if (!previousState.saved) { + return { saved: true }; + } else { + return null; + } + }); + } + } + + //TODO: check addRow() + addRow() { + this.clearMessages(); + this.setState(previousState => { + const data = [...previousState.data]; + const pair = { + [SRC_SECTOR]: {}, + [DST_SECTOR]: {}, + id: Math.random() * -1 + }; + data.push(pair); + setTimeout(() => (window.scrollTo(0, document.body.scrollHeight)), 500); + return { data, unsavedChanges: true, adding: true }; + }); + + } + onRowChange() {} + + remove(row) {} + + clearMessages() { + this.setState({ + validationErrors: undefined, updatedActivities: false, blockUI: false + }); + } + + saveAll() {} + + onChangeMainScheme(type, scheme) { + const { translations, trnPrefix } = this.context; + const { data, src, dst } = this.state; + this.src_ = src; + this.dst_ = dst; + + const newScheme = (scheme && scheme.length > 0) ? scheme[0] : {}; + const oldScheme = (this.state[type] ? this.state[type] : {}); + let autoAddRow = false; + + if (oldScheme.id !== newScheme.id) { + if (oldScheme.id !== undefined) { + if (data.length === 0 || window.confirm(translations[`${trnPrefix}warning_on_change_main_sector`])) { + if (newScheme.id !== undefined) { + // Old Scheme Sector -> New Sector. + this.setState(previousState => ({ [type]: newScheme })); + this[`${type}_`] = newScheme; + autoAddRow = true; + } else { + // Old Scheme Sector -> Nothing. + this.setState(previousState => ({ [type]: undefined })); + this[`${type}_`] = undefined; + } + this.clearAll(); + } else { + // Revert to previous Sector. + this.setState(previousState => previousState); + // TODO: set focus in the selector. + } + } else { + // Nothing -> Program. + this.setState(previousState => ({ [type]: newScheme })); + this[`${type}_`] = newScheme; + autoAddRow = true; + } + } + + // Note src_ and dst_ because setState() is not immediate. + if (this.src_ && this.dst_ && autoAddRow) { + this.addRow(); + } + } + + clearAll() { + this.setState({ + data: [] + }); + } + + revertAllChanges() { + window.location.reload(); + } + + render() { + const { data, validationErrors, src, dst, updatedActivities, unsavedChanges, saved, level } = this.state; + const { error, pending, translations, updating, errorUpdating, saving } = this.props; + + const { trnPrefix } = this.context; + const messages = []; + if (error) { + messages.push({ isError: true, text: error.toString() }); + } + if (validationErrors) { + messages.push({ isError: true, text: validationErrors }); + } + if (!saving && !error && !unsavedChanges && saved) { + messages.push({ isError: false, text: translations[`${trnPrefix}notification-saved-ok`] }); + } + + //TODO: check this flags commented + // if (updatedActivities) { + // messages.push({ + // isError: false, + // text: translations[`${trnPrefix}update-activities-successful`] + // }); + // } + // if (updating) { + // messages.push({ + // isError: false, + // text: translations[`${trnPrefix}update-activities-wait`] + // }); + // } + if (errorUpdating) { + messages.push({ + isError: true, + text: errorUpdating + }); + } + + return ( +
+ + + +
+ ); + } + +} + +FormSectors.contextType = SectorMappingContext; + +FormSectors.propTypes = { + translations: PropTypes.object.isRequired, + error: PropTypes.object, + pending: PropTypes.bool, + updating: PropTypes.bool, + errorUpdating: PropTypes.string, + _saveSectorMapping: PropTypes.func.isRequired, // _saveNDD + //_updateActivities: PropTypes.func.isRequired, + saving: PropTypes.bool.isRequired +}; + +FormSectors.defaultProps = { + error: undefined, + pending: false, + updating: false, + errorUpdating: null +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations, + // error: sendNDDError(state.saveNDDReducer), + // saving: sendNDDSaving(state.saveNDDReducer), + // pending: sendNDDPending(state.saveNDDReducer), + // updating: updateActivitiesPending(state.updateActivitiesReducer), + // errorUpdating: updateActivitiesError(state.updateActivitiesReducer) +}); +const mapDispatchToProps = dispatch => bindActionCreators({ + // _saveNDD: saveNDD, + // _updateActivities: updateActivities +}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(FormSectors); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx new file mode 100644 index 00000000000..8ed0a5d5d27 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx @@ -0,0 +1,94 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import {SectorMappingContext} from './Startup'; +import './css/style.css'; + +class Header extends Component { + render() { + const { + translations, onAddRow, onSaveAll, onRevertAll, src, dst, busy, dataPresent, unsavedChanges + } = this.props; + //onUpdateActivities + const { trnPrefix, isIndirect } = this.context; + return ( +
+
+
+ {(src && dst) ? ( + <> + + + {translations[`${trnPrefix}add-new`]} + {' '} + + + {translations[`${trnPrefix}insert-data`]} + + + ) : null} + / + {`* ${translations[`${trnPrefix}required-fields`]}`} + + + + {/*{isIndirect ? (*/} + {/* */} + {/* {translations[`${trnPrefix}button-update-activities`]}*/} + {/* */} + {/*) : null}*/} + +
+
+
+ ); + } +} + +Header.contextType = SectorMappingContext; + +Header.propTypes = { + onAddRow: PropTypes.func.isRequired, + onSaveAll: PropTypes.func.isRequired, + onRevertAll: PropTypes.func.isRequired, + translations: PropTypes.object.isRequired, + onUpdateActivities: PropTypes.func.isRequired, + src: PropTypes.object, + dst: PropTypes.object, + busy: PropTypes.bool.isRequired, + dataPresent: PropTypes.bool, + unsavedChanges: PropTypes.bool.isRequired +}; + +Header.defaultProps = { + src: undefined, + dst: undefined, + dataPresent: false +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); + +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(Header); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx new file mode 100644 index 00000000000..5d06bbdaa6b --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx @@ -0,0 +1,111 @@ +import React, { Component } from 'react'; +import './css/style.css'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import { SectorMappingContext } from './Startup'; +import { + getSectorMappings, + getSectorMappingError, + getSectorMappingPending, + getSchemes, + getSchemesPending +} from '../reducers/startupReducer'; +import fetchSectorMappings from "../actions/fetchSectorMappings"; +import fetchSchemes from "../actions/fetchSchemes"; +import fetchLayout from "../actions/fetchLayout"; +import fetchSettings from "../actions/fetchSettings"; +import BlockUI from "./common/BlockUI"; +import FormSectors from "./FormSectors"; + + +class Main extends Component { + constructor(props) { + super(props); + this.shouldComponentRender = this.shouldComponentRender.bind(this); + this.state = {}; + } + + componentDidMount() { + const { + _fetchSectorMappings, _fetchSchemes, api, _fetchLayout, _fetchSettings + } = this.props; + + _fetchSettings().then(settings => { + _fetchLayout().then(layout => { + if (layout && layout.logged && layout.administratorMode === true) { + _fetchSectorMappings(api.mappingConfig); + _fetchSchemes(api.allSchemes); + + this.setState({ isSuperAdmin: layout.email.indexOf('super') === 0, settings }); + } else { + window.location.replace('/login.do'); + } + }).catch(e => console.error(e)); + }).catch(e => console.error(e)); + } + + // pendingNDD -> pendingSectorMapping + // pendingPrograms -> pendingSectors + shouldComponentRender() { + const { pendingSectorMapping, pendingSectors } = this.props; + return !pendingSectorMapping && !pendingSectors; + } + + render() { + // ndd -> mappings + const { mappings, schemes, api, trnPrefix } = this.props; + const { translations } = this.context; + const { isSuperAdmin, settings } = this.state; + + if (!this.shouldComponentRender() || mappings.length === 0) { + return
{translations[`${trnPrefix}loading`]}
; + } else { + return ( +
+ +
+
+ +
+
+ {/**/} + +
+
+ ); + } + + } +} + +Main.contextType = SectorMappingContext; + +Main.propTypes = { + _fetchSectorMappings: PropTypes.func.isRequired, // _fetchNDD + _fetchSchemes: PropTypes.func.isRequired, // _fetchPrograms + _fetchLayout: PropTypes.func.isRequired, + _fetchSettings: PropTypes.func.isRequired, + api: PropTypes.object.isRequired +}; + +const mapStateToProps = state => ({ + error: getSectorMappingError(state.startupReducer), + mappings: getSectorMappings(state.startupReducer), + schemes: getSchemes(state.startupReducer), + pendingMappings: getSectorMappingPending(state.startupReducer), + pendingSchemes: getSchemesPending(state.startupReducer), + translations: state.translationsReducer.translations, + //indirectProgramUpdatePending: state.updateActivitiesReducer.indirectProgramUpdatePending +}); + +const mapDispatchToProps = dispatch => bindActionCreators({ + _fetchSectorMappings: fetchSectorMappings, + _fetchSchemes: fetchSchemes, + _fetchLayout: fetchLayout, + _fetchSettings: fetchSettings +}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(Main); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Notifications.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Notifications.jsx new file mode 100644 index 00000000000..189b50892dd --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Notifications.jsx @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import {SectorMappingContext} from './Startup'; +import './css/style.css'; + +class Notifications extends Component { + render() { + const { messages } = this.props; + if (messages && messages.length > 0) { + return ( +
+
    + {messages.map(message => { + const className = message.isError ? 'error-color' : 'success-color'; + return
  • {message.text}
  • ; + })} +
+
+ ); + } + return null; + } +} + +Notifications.contextType = SectorMappingContext; + +Notifications.propTypes = { + messages: PropTypes.array.isRequired +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(Notifications); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx new file mode 100644 index 00000000000..0f9bcf44641 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx @@ -0,0 +1,33 @@ +import React, { useState } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +//import AdminNDDIndirectProgramApp from '../indirect'; +import AdminSectorMappingApp from '../sector'; + +// eslint-disable-next-line no-unused-vars +import styles from './css/style.css'; + +const SectorMappingAdminNavigator = ({ translations }) => { + // const [key, setKey] = useState('indirect'); + //const trnPrefix = 'amp.admin.sectorMapping:'; + return ( + // + <> + + + + ); +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations, +}); + +SectorMappingAdminNavigator.propTypes = { + translations: PropTypes.object.isRequired +}; + +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(SectorMappingAdminNavigator); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx new file mode 100644 index 00000000000..5514d0405e0 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx @@ -0,0 +1,77 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import {SectorMappingContext} from './Startup'; +import './css/style.css'; +import { TYPE_SRC, TYPE_DST } from '../constants/Constants'; +import HelpTooltip from "./common/HelpTooltip"; +import Select from './Select'; + +class SectorsHeader extends Component { + + render() { + const { translations, schemes, trnPrefix } = this.context; + const { src, dst, onChange, busy } = this.props + + if (schemes) { + return ( + + + + + + + +
+ + + +
+ ); + } else { + return ( +
+ ); + } + } +} + +SectorsHeader.contextType = SectorMappingContext; + +SectorsHeader.propTypes = { + src: PropTypes.object, + dst: PropTypes.object, + onChange: PropTypes.func.isRequired, + busy: PropTypes.bool.isRequired, + level: PropTypes.number +}; + +SectorsHeader.defaultProps = { + src: undefined, + dst: undefined, + level: 1 +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); + +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(SectorsHeader); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx new file mode 100644 index 00000000000..d4874c61cc9 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx @@ -0,0 +1,70 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { Typeahead } from 'react-bootstrap-typeahead'; +import PropTypes from 'prop-types'; +import {SectorMappingContext} from './Startup'; +import './css/Typeahead.css'; +import './css/style.css'; +import RequiredMark from '../../ndd/components/common/RequiredMark'; + +class Select extends Component { + constructor(props) { + super(props); + this.drawSelector = this.drawSelector.bind(this); + } + onChangeSelect(selected) { + const { onChange, level } = this.props; + onChange(selected, level); + } + + drawSelector() { + const { options, placeholder, selected, disabled } = this.props; + const { translations, trnPrefix } = this.context; + const isValid = (selected && selected.length === 1); + const sortedOptions = options ? options.sort((a, b) => a.value.localeCompare(b.value)) : []; + return ( + + ); + } + + render() { + const { label } = this.props; + return ( +
+ {label} + + {this.drawSelector()} +
+ ); + } +} + +Select.contextType = SectorMappingContext; + +Select.propTypes = { + options: PropTypes.array.isRequired, + label: PropTypes.string.isRequired, + placeholder: PropTypes.string.isRequired, + selected: PropTypes.array, + onChange: PropTypes.func.isRequired, + level: PropTypes.number.isRequired, + disabled: PropTypes.bool.isRequired +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(Select); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx new file mode 100644 index 00000000000..130548de02f --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx @@ -0,0 +1,50 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import PropTypes from 'prop-types'; +import fetchTranslations from '../../../../utils/actions/fetchTranslations'; +import { Loading } from '../../../../utils/components/Loading'; + +export const SectorMappingContext = React.createContext(); + +/** + * Component used to load everything we need before launching the APP + */ +class Startup extends Component { + componentDidMount() { + const { defaultTrnPack, _fetchTranslations } = this.props; + _fetchTranslations(defaultTrnPack); + } + + render() { + const { children, translations, translationPending, api } = this.props; + if (translationPending) { + return (); + } else { + document.title = translations['amp.admin.ndd:page-title']; + return ( + + {children} + + ); + } + } +} + +const mapStateToProps = state => ({ + translationPending: state.translationsReducer.pending, + translations: state.translationsReducer.translations +}); + +const mapDispatchToProps = dispatch => bindActionCreators({ _fetchTranslations: fetchTranslations }, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(Startup); + +Startup.propTypes = { + translationPending: PropTypes.bool.isRequired, + translations: PropTypes.object.isRequired, + children: PropTypes.object.isRequired, + _fetchTranslations: PropTypes.func.isRequired, + api: PropTypes.object.isRequired, + defaultTrnPack: PropTypes.object.isRequired, +}; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.css b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.css new file mode 100644 index 00000000000..7b522a902d6 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.css @@ -0,0 +1,85 @@ +.loading-indicator { + text-align: center; +} + +.block-ui { + position: relative; + min-height: 3em; +} + +.block-ui-container { + position: absolute; + z-index: 1010; + top: 0; + right: 0; + bottom: 0; + left: 0; + height: 100%; + width: 100%; + min-height: 2em; + cursor: wait; + overflow: hidden; +} + +.block-ui-container:focus { + outline: none; +} + +.block-ui-overlay { + width: 100%; + height: 100%; + opacity: 0.75; + filter: alpha(opacity=50); + background-color: rgb(184, 184, 184); +} + +.block-ui-message-container { + position: absolute; + top: 50%; + left: 0; + right: 0; + text-align: center; + transform: translateY(-50%); + z-index: 10001; +} + +.block-ui-message { + color: #333; + background: none; + z-index: 1011; +} + +#indicator { + width: 100px; + height: 100px; + position: absolute; + top: calc(50% - 50px); + left: calc(50% - 50px); + animation: spin 1s linear infinite; +} + +#circle { + fill: none; + stroke: rgba(255, 255, 255, 0.5); + stroke-linecap: round; + stroke-width: 4; + animation: draw 3s ease-in-out infinite; +} + +@keyframes draw { + 0% { + stroke-dasharray: 20, 282.6; + } + 50% { + stroke-dasharray: 200, 282.6; + } + 100% { + stroke-dasharray: 20, 282.6; + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.jsx new file mode 100644 index 00000000000..9ac485008f0 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/BlockUI.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import './BlockUI.css'; + +const BlockUI = props => { + if (!props.blocking) { + return ''; + } else { + return ( +
+
+
+
+

{props.title}

+
+ + + +
+
+
+
+ ); + } +}; + +BlockUI.defaultProps = { + blocking: false, + title: 'Loading' +}; + +export default BlockUI; \ No newline at end of file diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx new file mode 100644 index 00000000000..2251af8a961 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx @@ -0,0 +1,41 @@ +import React, { Component } from 'react'; +import { OverlayTrigger, Tooltip } from 'react-bootstrap'; +import PropTypes from 'prop-types'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import {SectorMappingContext} from '../Startup'; + +// eslint-disable-next-line no-unused-vars +import styles from '../css/style.css'; + +class HelpTooltip extends Component { + render() { + const { translations, labelKey } = this.props; + const tooltip = ( + + {translations[labelKey]} + + ); + return ( + + {/* eslint-disable-next-line jsx-a11y/alt-text */} + + + ); + } +} + +HelpTooltip.contextType = SectorMappingContext; + +HelpTooltip.propTypes = { + translations: PropTypes.object.isRequired, + labelKey: PropTypes.string.isRequired +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(HelpTooltip); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/Typeahead.css b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/Typeahead.css new file mode 100644 index 00000000000..086e29e68d1 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/Typeahead.css @@ -0,0 +1,201 @@ +.rbt .rbt-input-main::-ms-clear { + display: none; +} + +/** + * Menu + */ +.rbt-menu { + margin-bottom: 2px; +} + +.rbt-menu > .dropdown-item { + overflow: hidden; + text-overflow: ellipsis; +} + +.rbt-menu > .dropdown-item:focus { + outline: none; +} + +.rbt-menu-pagination-option { + text-align: center; +} + +/** + * Multi-select Input + */ +.rbt-input-multi { + cursor: text; + overflow: hidden; + position: relative; +} + +.rbt-input-multi.focus { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); + color: #495057; + outline: 0; +} + +.rbt-input-multi.form-control { + height: auto; +} + +.rbt-input-multi.form-control[disabled] { + background-color: #e9ecef; + opacity: 1; +} + +.rbt-input-multi.is-invalid.focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); +} + +.rbt-input-multi.is-valid.focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); +} + +.rbt-input-multi input::-moz-placeholder { + color: #6c757d; + opacity: 1; +} + +.rbt-input-multi input:-ms-input-placeholder { + color: #6c757d; +} + +.rbt-input-multi input::-webkit-input-placeholder { + color: #6c757d; +} + +.rbt-input-multi .rbt-input-wrapper { + align-items: flex-start; + display: flex; + flex-wrap: wrap; + margin-bottom: -4px; + margin-top: -1px; + overflow: hidden; +} + +.rbt-input-multi .rbt-input-main { + margin: 1px 0 4px; +} + +/** + * Close Button + */ +.rbt-close { + z-index: 1; +} + +.rbt-close-lg { + font-size: 24px; +} + +/** + * Token + */ +.rbt-token { + background-color: #e7f4ff; + border: 0; + border-radius: .25rem; + color: #007bff; + display: inline-block; + line-height: 1em; + margin: 1px 3px 2px 0; + padding: 4px 7px; + position: relative; +} + +.rbt-token-disabled { + background-color: rgba(0, 0, 0, 0.1); + color: #495057; + pointer-events: none; +} + +.rbt-token-removeable { + cursor: pointer; + padding-right: 21px; +} + +.rbt-token-active { + background-color: #007bff; + color: #fff; + outline: none; + text-decoration: none; +} + +.rbt-token .rbt-token-remove-button { + bottom: 0; + color: inherit; + font-size: inherit; + font-weight: normal; + opacity: 1; + outline: none; + padding: 3px 7px; + position: absolute; + right: 0; + text-shadow: none; + top: -2px; +} + +/** + * Loader + CloseButton container + */ +.rbt-aux { + align-items: center; + display: flex; + bottom: 0; + justify-content: center; + pointer-events: none; + /* Don't block clicks on the input */ + position: absolute; + right: 0; + top: 0; + width: 34px; +} + +.rbt-aux-lg { + width: 46px; +} + +.rbt-aux .rbt-close { + margin-top: -4px; + pointer-events: auto; + /* Override pointer-events: none; above */ +} + +.has-aux .rbt-input { + padding-right: 34px; +} + +.rbt-highlight-text { + background-color: inherit; + color: inherit; + font-weight: bold; + padding: 0; +} + +/** + * Input Groups + */ +.input-group > .rbt { + flex: 1; +} + +.input-group > .rbt .rbt-input-hint, +.input-group > .rbt .rbt-aux { + z-index: 5; +} + +.input-group > .rbt:not(:first-child) .form-control { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .rbt:not(:last-child) .form-control { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/bootstrap-4.4.1.css b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/bootstrap-4.4.1.css new file mode 100644 index 00000000000..ad521f86718 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/bootstrap-4.4.1.css @@ -0,0 +1,10382 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #007bff; + --secondary: #6c757d; + --success: #28a745; + --info: #17a2b8; + --warning: #ffc107; + --danger: #dc3545; + --light: #f8f9fa; + --dark: #343a40; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace +} + +*, ::after, ::before { + box-sizing: border-box +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff +} + +[tabindex="-1"]:focus:not(:focus-visible) { + outline: 0 !important +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: .5rem +} + +p { + margin-top: 0; + margin-bottom: 1rem +} + +abbr[data-original-title], abbr[title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + -webkit-text-decoration-skip-ink: none; + text-decoration-skip-ink: none +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit +} + +dl, ol, ul { + margin-top: 0; + margin-bottom: 1rem +} + +ol ol, ol ul, ul ol, ul ul { + margin-bottom: 0 +} + +dt { + font-weight: 700 +} + +dd { + margin-bottom: .5rem; + margin-left: 0 +} + +blockquote { + margin: 0 0 1rem +} + +b, strong { + font-weight: bolder +} + +small { + font-size: 80% +} + +sub, sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +a { + color: #007bff; + text-decoration: none; + background-color: transparent +} + +a:hover { + color: #0056b3; + text-decoration: underline +} + +a:not([href]) { + color: inherit; + text-decoration: none +} + +a:not([href]):hover { + color: inherit; + text-decoration: none +} + +code, kbd, pre, samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto +} + +figure { + margin: 0 0 1rem +} + +img { + vertical-align: middle; + border-style: none +} + +svg { + overflow: hidden; + vertical-align: middle +} + +table { + border-collapse: collapse +} + +caption { + padding-top: .75rem; + padding-bottom: .75rem; + color: #6c757d; + text-align: left; + caption-side: bottom +} + +th { + text-align: inherit +} + +label { + display: inline-block; + margin-bottom: .5rem +} + +button { + border-radius: 0 +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color +} + +button, input, optgroup, select, textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit +} + +button, input { + overflow: visible +} + +button, select { + text-transform: none +} + +select { + word-wrap: normal +} + +[type=button], [type=reset], [type=submit], button { + -webkit-appearance: button +} + +[type=button]:not(:disabled), [type=reset]:not(:disabled), [type=submit]:not(:disabled), button:not(:disabled) { + cursor: pointer +} + +[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner { + padding: 0; + border-style: none +} + +input[type=checkbox], input[type=radio] { + box-sizing: border-box; + padding: 0 +} + +input[type=date], input[type=datetime-local], input[type=month], input[type=time] { + -webkit-appearance: listbox +} + +textarea { + overflow: auto; + resize: vertical +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0 +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal +} + +progress { + vertical-align: baseline +} + +[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + outline-offset: -2px; + -webkit-appearance: none +} + +[type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button +} + +output { + display: inline-block +} + +summary { + display: list-item; + cursor: pointer +} + +template { + display: none +} + +[hidden] { + display: none !important +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + margin-bottom: .5rem; + font-weight: 500; + line-height: 1.2 +} + +.h1, h1 { + font-size: 2.5rem +} + +.h2, h2 { + font-size: 2rem +} + +.h3, h3 { + font-size: 1.75rem +} + +.h4, h4 { + font-size: 1.5rem +} + +.h5, h5 { + font-size: 1.25rem +} + +.h6, h6 { + font-size: 1rem +} + +.lead { + font-size: 1.25rem; + font-weight: 300 +} + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2 +} + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2 +} + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2 +} + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2 +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, .1) +} + +.small, small { + font-size: 80%; + font-weight: 400 +} + +.mark, mark { + padding: .2em; + background-color: #fcf8e3 +} + +.list-unstyled { + padding-left: 0; + list-style: none +} + +.list-inline { + padding-left: 0; + list-style: none +} + +.list-inline-item { + display: inline-block +} + +.list-inline-item:not(:last-child) { + margin-right: .5rem +} + +.initialism { + font-size: 90%; + text-transform: uppercase +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d +} + +.blockquote-footer::before { + content: "\2014\00A0" +} + +.img-fluid { + max-width: 100%; + height: auto +} + +.img-thumbnail { + padding: .25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: .25rem; + max-width: 100%; + height: auto +} + +.figure { + display: inline-block +} + +.figure-img { + margin-bottom: .5rem; + line-height: 1 +} + +.figure-caption { + font-size: 90%; + color: #6c757d +} + +code { + font-size: 87.5%; + color: #e83e8c; + word-wrap: break-word +} + +a > code { + color: inherit +} + +kbd { + padding: .2rem .4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: .2rem +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700 +} + +pre { + display: block; + font-size: 87.5%; + color: #212529 +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +@media (min-width: 576px) { + .container { + max-width: 540px + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px + } +} + +.container-fluid, .container-lg, .container-md, .container-sm, .container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto +} + +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px + } +} + +@media (min-width: 768px) { + .container, .container-md, .container-sm { + max-width: 720px + } +} + +@media (min-width: 992px) { + .container, .container-lg, .container-md, .container-sm { + max-width: 960px + } +} + +@media (min-width: 1200px) { + .container, .container-lg, .container-md, .container-sm, .container-xl { + max-width: 1140px + } +} + +.row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px +} + +.no-gutters { + margin-right: 0; + margin-left: 0 +} + +.no-gutters > .col, .no-gutters > [class*=col-] { + padding-right: 0; + padding-left: 0 +} + +.col, .col-1, .col-10, .col-11, .col-12, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-auto, .col-lg, .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-auto, .col-md, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-auto, .col-sm, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px +} + +.col { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100% +} + +.row-cols-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% +} + +.row-cols-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% +} + +.row-cols-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% +} + +.row-cols-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% +} + +.row-cols-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20% +} + +.row-cols-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% +} + +.col-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100% +} + +.col-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333% +} + +.col-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% +} + +.col-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% +} + +.col-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% +} + +.col-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667% +} + +.col-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% +} + +.col-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333% +} + +.col-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667% +} + +.col-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75% +} + +.col-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333% +} + +.col-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667% +} + +.col-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% +} + +.order-first { + -ms-flex-order: -1; + order: -1 +} + +.order-last { + -ms-flex-order: 13; + order: 13 +} + +.order-0 { + -ms-flex-order: 0; + order: 0 +} + +.order-1 { + -ms-flex-order: 1; + order: 1 +} + +.order-2 { + -ms-flex-order: 2; + order: 2 +} + +.order-3 { + -ms-flex-order: 3; + order: 3 +} + +.order-4 { + -ms-flex-order: 4; + order: 4 +} + +.order-5 { + -ms-flex-order: 5; + order: 5 +} + +.order-6 { + -ms-flex-order: 6; + order: 6 +} + +.order-7 { + -ms-flex-order: 7; + order: 7 +} + +.order-8 { + -ms-flex-order: 8; + order: 8 +} + +.order-9 { + -ms-flex-order: 9; + order: 9 +} + +.order-10 { + -ms-flex-order: 10; + order: 10 +} + +.order-11 { + -ms-flex-order: 11; + order: 11 +} + +.order-12 { + -ms-flex-order: 12; + order: 12 +} + +.offset-1 { + margin-left: 8.333333% +} + +.offset-2 { + margin-left: 16.666667% +} + +.offset-3 { + margin-left: 25% +} + +.offset-4 { + margin-left: 33.333333% +} + +.offset-5 { + margin-left: 41.666667% +} + +.offset-6 { + margin-left: 50% +} + +.offset-7 { + margin-left: 58.333333% +} + +.offset-8 { + margin-left: 66.666667% +} + +.offset-9 { + margin-left: 75% +} + +.offset-10 { + margin-left: 83.333333% +} + +.offset-11 { + margin-left: 91.666667% +} + +@media (min-width: 576px) { + .col-sm { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100% + } + + .row-cols-sm-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .row-cols-sm-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .row-cols-sm-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .row-cols-sm-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .row-cols-sm-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20% + } + + .row-cols-sm-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-sm-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-sm-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333% + } + + .col-sm-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-sm-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .col-sm-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .col-sm-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667% + } + + .col-sm-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .col-sm-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333% + } + + .col-sm-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667% + } + + .col-sm-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75% + } + + .col-sm-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333% + } + + .col-sm-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667% + } + + .col-sm-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .order-sm-first { + -ms-flex-order: -1; + order: -1 + } + + .order-sm-last { + -ms-flex-order: 13; + order: 13 + } + + .order-sm-0 { + -ms-flex-order: 0; + order: 0 + } + + .order-sm-1 { + -ms-flex-order: 1; + order: 1 + } + + .order-sm-2 { + -ms-flex-order: 2; + order: 2 + } + + .order-sm-3 { + -ms-flex-order: 3; + order: 3 + } + + .order-sm-4 { + -ms-flex-order: 4; + order: 4 + } + + .order-sm-5 { + -ms-flex-order: 5; + order: 5 + } + + .order-sm-6 { + -ms-flex-order: 6; + order: 6 + } + + .order-sm-7 { + -ms-flex-order: 7; + order: 7 + } + + .order-sm-8 { + -ms-flex-order: 8; + order: 8 + } + + .order-sm-9 { + -ms-flex-order: 9; + order: 9 + } + + .order-sm-10 { + -ms-flex-order: 10; + order: 10 + } + + .order-sm-11 { + -ms-flex-order: 11; + order: 11 + } + + .order-sm-12 { + -ms-flex-order: 12; + order: 12 + } + + .offset-sm-0 { + margin-left: 0 + } + + .offset-sm-1 { + margin-left: 8.333333% + } + + .offset-sm-2 { + margin-left: 16.666667% + } + + .offset-sm-3 { + margin-left: 25% + } + + .offset-sm-4 { + margin-left: 33.333333% + } + + .offset-sm-5 { + margin-left: 41.666667% + } + + .offset-sm-6 { + margin-left: 50% + } + + .offset-sm-7 { + margin-left: 58.333333% + } + + .offset-sm-8 { + margin-left: 66.666667% + } + + .offset-sm-9 { + margin-left: 75% + } + + .offset-sm-10 { + margin-left: 83.333333% + } + + .offset-sm-11 { + margin-left: 91.666667% + } +} + +@media (min-width: 768px) { + .col-md { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100% + } + + .row-cols-md-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .row-cols-md-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .row-cols-md-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .row-cols-md-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .row-cols-md-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20% + } + + .row-cols-md-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-md-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-md-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333% + } + + .col-md-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-md-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .col-md-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .col-md-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667% + } + + .col-md-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .col-md-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333% + } + + .col-md-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667% + } + + .col-md-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75% + } + + .col-md-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333% + } + + .col-md-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667% + } + + .col-md-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .order-md-first { + -ms-flex-order: -1; + order: -1 + } + + .order-md-last { + -ms-flex-order: 13; + order: 13 + } + + .order-md-0 { + -ms-flex-order: 0; + order: 0 + } + + .order-md-1 { + -ms-flex-order: 1; + order: 1 + } + + .order-md-2 { + -ms-flex-order: 2; + order: 2 + } + + .order-md-3 { + -ms-flex-order: 3; + order: 3 + } + + .order-md-4 { + -ms-flex-order: 4; + order: 4 + } + + .order-md-5 { + -ms-flex-order: 5; + order: 5 + } + + .order-md-6 { + -ms-flex-order: 6; + order: 6 + } + + .order-md-7 { + -ms-flex-order: 7; + order: 7 + } + + .order-md-8 { + -ms-flex-order: 8; + order: 8 + } + + .order-md-9 { + -ms-flex-order: 9; + order: 9 + } + + .order-md-10 { + -ms-flex-order: 10; + order: 10 + } + + .order-md-11 { + -ms-flex-order: 11; + order: 11 + } + + .order-md-12 { + -ms-flex-order: 12; + order: 12 + } + + .offset-md-0 { + margin-left: 0 + } + + .offset-md-1 { + margin-left: 8.333333% + } + + .offset-md-2 { + margin-left: 16.666667% + } + + .offset-md-3 { + margin-left: 25% + } + + .offset-md-4 { + margin-left: 33.333333% + } + + .offset-md-5 { + margin-left: 41.666667% + } + + .offset-md-6 { + margin-left: 50% + } + + .offset-md-7 { + margin-left: 58.333333% + } + + .offset-md-8 { + margin-left: 66.666667% + } + + .offset-md-9 { + margin-left: 75% + } + + .offset-md-10 { + margin-left: 83.333333% + } + + .offset-md-11 { + margin-left: 91.666667% + } +} + +@media (min-width: 992px) { + .col-lg { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100% + } + + .row-cols-lg-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .row-cols-lg-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .row-cols-lg-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .row-cols-lg-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .row-cols-lg-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20% + } + + .row-cols-lg-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-lg-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-lg-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333% + } + + .col-lg-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-lg-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .col-lg-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .col-lg-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667% + } + + .col-lg-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .col-lg-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333% + } + + .col-lg-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667% + } + + .col-lg-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75% + } + + .col-lg-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333% + } + + .col-lg-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667% + } + + .col-lg-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .order-lg-first { + -ms-flex-order: -1; + order: -1 + } + + .order-lg-last { + -ms-flex-order: 13; + order: 13 + } + + .order-lg-0 { + -ms-flex-order: 0; + order: 0 + } + + .order-lg-1 { + -ms-flex-order: 1; + order: 1 + } + + .order-lg-2 { + -ms-flex-order: 2; + order: 2 + } + + .order-lg-3 { + -ms-flex-order: 3; + order: 3 + } + + .order-lg-4 { + -ms-flex-order: 4; + order: 4 + } + + .order-lg-5 { + -ms-flex-order: 5; + order: 5 + } + + .order-lg-6 { + -ms-flex-order: 6; + order: 6 + } + + .order-lg-7 { + -ms-flex-order: 7; + order: 7 + } + + .order-lg-8 { + -ms-flex-order: 8; + order: 8 + } + + .order-lg-9 { + -ms-flex-order: 9; + order: 9 + } + + .order-lg-10 { + -ms-flex-order: 10; + order: 10 + } + + .order-lg-11 { + -ms-flex-order: 11; + order: 11 + } + + .order-lg-12 { + -ms-flex-order: 12; + order: 12 + } + + .offset-lg-0 { + margin-left: 0 + } + + .offset-lg-1 { + margin-left: 8.333333% + } + + .offset-lg-2 { + margin-left: 16.666667% + } + + .offset-lg-3 { + margin-left: 25% + } + + .offset-lg-4 { + margin-left: 33.333333% + } + + .offset-lg-5 { + margin-left: 41.666667% + } + + .offset-lg-6 { + margin-left: 50% + } + + .offset-lg-7 { + margin-left: 58.333333% + } + + .offset-lg-8 { + margin-left: 66.666667% + } + + .offset-lg-9 { + margin-left: 75% + } + + .offset-lg-10 { + margin-left: 83.333333% + } + + .offset-lg-11 { + margin-left: 91.666667% + } +} + +@media (min-width: 1200px) { + .col-xl { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100% + } + + .row-cols-xl-1 > * { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .row-cols-xl-2 > * { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .row-cols-xl-3 > * { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .row-cols-xl-4 > * { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .row-cols-xl-5 > * { + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20% + } + + .row-cols-xl-6 > * { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-xl-auto { + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100% + } + + .col-xl-1 { + -ms-flex: 0 0 8.333333%; + flex: 0 0 8.333333%; + max-width: 8.333333% + } + + .col-xl-2 { + -ms-flex: 0 0 16.666667%; + flex: 0 0 16.666667%; + max-width: 16.666667% + } + + .col-xl-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25% + } + + .col-xl-4 { + -ms-flex: 0 0 33.333333%; + flex: 0 0 33.333333%; + max-width: 33.333333% + } + + .col-xl-5 { + -ms-flex: 0 0 41.666667%; + flex: 0 0 41.666667%; + max-width: 41.666667% + } + + .col-xl-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50% + } + + .col-xl-7 { + -ms-flex: 0 0 58.333333%; + flex: 0 0 58.333333%; + max-width: 58.333333% + } + + .col-xl-8 { + -ms-flex: 0 0 66.666667%; + flex: 0 0 66.666667%; + max-width: 66.666667% + } + + .col-xl-9 { + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75% + } + + .col-xl-10 { + -ms-flex: 0 0 83.333333%; + flex: 0 0 83.333333%; + max-width: 83.333333% + } + + .col-xl-11 { + -ms-flex: 0 0 91.666667%; + flex: 0 0 91.666667%; + max-width: 91.666667% + } + + .col-xl-12 { + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100% + } + + .order-xl-first { + -ms-flex-order: -1; + order: -1 + } + + .order-xl-last { + -ms-flex-order: 13; + order: 13 + } + + .order-xl-0 { + -ms-flex-order: 0; + order: 0 + } + + .order-xl-1 { + -ms-flex-order: 1; + order: 1 + } + + .order-xl-2 { + -ms-flex-order: 2; + order: 2 + } + + .order-xl-3 { + -ms-flex-order: 3; + order: 3 + } + + .order-xl-4 { + -ms-flex-order: 4; + order: 4 + } + + .order-xl-5 { + -ms-flex-order: 5; + order: 5 + } + + .order-xl-6 { + -ms-flex-order: 6; + order: 6 + } + + .order-xl-7 { + -ms-flex-order: 7; + order: 7 + } + + .order-xl-8 { + -ms-flex-order: 8; + order: 8 + } + + .order-xl-9 { + -ms-flex-order: 9; + order: 9 + } + + .order-xl-10 { + -ms-flex-order: 10; + order: 10 + } + + .order-xl-11 { + -ms-flex-order: 11; + order: 11 + } + + .order-xl-12 { + -ms-flex-order: 12; + order: 12 + } + + .offset-xl-0 { + margin-left: 0 + } + + .offset-xl-1 { + margin-left: 8.333333% + } + + .offset-xl-2 { + margin-left: 16.666667% + } + + .offset-xl-3 { + margin-left: 25% + } + + .offset-xl-4 { + margin-left: 33.333333% + } + + .offset-xl-5 { + margin-left: 41.666667% + } + + .offset-xl-6 { + margin-left: 50% + } + + .offset-xl-7 { + margin-left: 58.333333% + } + + .offset-xl-8 { + margin-left: 66.666667% + } + + .offset-xl-9 { + margin-left: 75% + } + + .offset-xl-10 { + margin-left: 83.333333% + } + + .offset-xl-11 { + margin-left: 91.666667% + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + color: #212529 +} + +.table td, .table th { + padding: .75rem; + vertical-align: top; + border-top: 1px solid #dee2e6 +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6 +} + +.table tbody + tbody { + border-top: 2px solid #dee2e6 +} + +.table-sm td, .table-sm th { + padding: .3rem +} + +.table-bordered { + border: 1px solid #dee2e6 +} + +.table-bordered td, .table-bordered th { + border: 1px solid #dee2e6 +} + +.table-bordered thead td, .table-bordered thead th { + border-bottom-width: 2px +} + +.table-borderless tbody + tbody, .table-borderless td, .table-borderless th, .table-borderless thead th { + border: 0 +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, .05) +} + +.table-hover tbody tr:hover { + color: #212529; + background-color: rgba(0, 0, 0, .075) +} + +.table-primary, .table-primary > td, .table-primary > th { + background-color: #b8daff +} + +.table-primary tbody + tbody, .table-primary td, .table-primary th, .table-primary thead th { + border-color: #7abaff +} + +.table-hover .table-primary:hover { + background-color: #9fcdff +} + +.table-hover .table-primary:hover > td, .table-hover .table-primary:hover > th { + background-color: #9fcdff +} + +.table-secondary, .table-secondary > td, .table-secondary > th { + background-color: #d6d8db +} + +.table-secondary tbody + tbody, .table-secondary td, .table-secondary th, .table-secondary thead th { + border-color: #b3b7bb +} + +.table-hover .table-secondary:hover { + background-color: #c8cbcf +} + +.table-hover .table-secondary:hover > td, .table-hover .table-secondary:hover > th { + background-color: #c8cbcf +} + +.table-success, .table-success > td, .table-success > th { + background-color: #c3e6cb +} + +.table-success tbody + tbody, .table-success td, .table-success th, .table-success thead th { + border-color: #8fd19e +} + +.table-hover .table-success:hover { + background-color: #b1dfbb +} + +.table-hover .table-success:hover > td, .table-hover .table-success:hover > th { + background-color: #b1dfbb +} + +.table-info, .table-info > td, .table-info > th { + background-color: #bee5eb +} + +.table-info tbody + tbody, .table-info td, .table-info th, .table-info thead th { + border-color: #86cfda +} + +.table-hover .table-info:hover { + background-color: #abdde5 +} + +.table-hover .table-info:hover > td, .table-hover .table-info:hover > th { + background-color: #abdde5 +} + +.table-warning, .table-warning > td, .table-warning > th { + background-color: #ffeeba +} + +.table-warning tbody + tbody, .table-warning td, .table-warning th, .table-warning thead th { + border-color: #ffdf7e +} + +.table-hover .table-warning:hover { + background-color: #ffe8a1 +} + +.table-hover .table-warning:hover > td, .table-hover .table-warning:hover > th { + background-color: #ffe8a1 +} + +.table-danger, .table-danger > td, .table-danger > th { + background-color: #f5c6cb +} + +.table-danger tbody + tbody, .table-danger td, .table-danger th, .table-danger thead th { + border-color: #ed969e +} + +.table-hover .table-danger:hover { + background-color: #f1b0b7 +} + +.table-hover .table-danger:hover > td, .table-hover .table-danger:hover > th { + background-color: #f1b0b7 +} + +.table-light, .table-light > td, .table-light > th { + background-color: #fdfdfe +} + +.table-light tbody + tbody, .table-light td, .table-light th, .table-light thead th { + border-color: #fbfcfc +} + +.table-hover .table-light:hover { + background-color: #ececf6 +} + +.table-hover .table-light:hover > td, .table-hover .table-light:hover > th { + background-color: #ececf6 +} + +.table-dark, .table-dark > td, .table-dark > th { + background-color: #c6c8ca +} + +.table-dark tbody + tbody, .table-dark td, .table-dark th, .table-dark thead th { + border-color: #95999c +} + +.table-hover .table-dark:hover { + background-color: #b9bbbe +} + +.table-hover .table-dark:hover > td, .table-hover .table-dark:hover > th { + background-color: #b9bbbe +} + +.table-active, .table-active > td, .table-active > th { + background-color: rgba(0, 0, 0, .075) +} + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, .075) +} + +.table-hover .table-active:hover > td, .table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, .075) +} + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55 +} + +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6 +} + +.table-dark { + color: #fff; + background-color: #343a40 +} + +.table-dark td, .table-dark th, .table-dark thead th { + border-color: #454d55 +} + +.table-dark.table-bordered { + border: 0 +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, .05) +} + +.table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: rgba(255, 255, 255, .075) +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-sm > .table-bordered { + border: 0 + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-md > .table-bordered { + border: 0 + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-lg > .table-bordered { + border: 0 + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch + } + + .table-responsive-xl > .table-bordered { + border: 0 + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch +} + +.table-responsive > .table-bordered { + border: 0 +} + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + padding: .375rem .75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: .25rem; + transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .form-control { + transition: none + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0 +} + +.form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057 +} + +.form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.form-control::-webkit-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::-moz-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control:-ms-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::-ms-input-placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control::placeholder { + color: #6c757d; + opacity: 1 +} + +.form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1 +} + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff +} + +.form-control-file, .form-control-range { + display: block; + width: 100% +} + +.col-form-label { + padding-top: calc(.375rem + 1px); + padding-bottom: calc(.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5 +} + +.col-form-label-lg { + padding-top: calc(.5rem + 1px); + padding-bottom: calc(.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5 +} + +.col-form-label-sm { + padding-top: calc(.25rem + 1px); + padding-bottom: calc(.25rem + 1px); + font-size: .875rem; + line-height: 1.5 +} + +.form-control-plaintext { + display: block; + width: 100%; + padding: .375rem 0; + margin-bottom: 0; + font-size: 1rem; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0 +} + +.form-control-plaintext.form-control-lg, .form-control-plaintext.form-control-sm { + padding-right: 0; + padding-left: 0 +} + +.form-control-sm { + height: calc(1.5em + .5rem + 2px); + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +select.form-control[multiple], select.form-control[size] { + height: auto +} + +textarea.form-control { + height: auto +} + +.form-group { + margin-bottom: 1rem +} + +.form-text { + display: block; + margin-top: .25rem +} + +.form-row { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px +} + +.form-row > .col, .form-row > [class*=col-] { + padding-right: 5px; + padding-left: 5px +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem +} + +.form-check-input { + position: absolute; + margin-top: .3rem; + margin-left: -1.25rem +} + +.form-check-input:disabled ~ .form-check-label, .form-check-input[disabled] ~ .form-check-label { + color: #6c757d +} + +.form-check-label { + margin-bottom: 0 +} + +.form-check-inline { + display: -ms-inline-flexbox; + display: inline-flex; + -ms-flex-align: center; + align-items: center; + padding-left: 0; + margin-right: .75rem +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: .3125rem; + margin-left: 0 +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: .25rem; + font-size: 80%; + color: #28a745 +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .25rem .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, .9); + border-radius: .25rem +} + +.is-valid ~ .valid-feedback, .is-valid ~ .valid-tooltip, .was-validated :valid ~ .valid-feedback, .was-validated :valid ~ .valid-tooltip { + display: block +} + +.form-control.is-valid, .was-validated .form-control:valid { + border-color: #28a745; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(.375em + .1875rem) center; + background-size: calc(.75em + .375rem) calc(.75em + .375rem) +} + +.form-control.is-valid:focus, .was-validated .form-control:valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .25) +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem) +} + +.custom-select.is-valid, .was-validated .custom-select:valid { + border-color: #28a745; + padding-right: calc(.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) +} + +.custom-select.is-valid:focus, .was-validated .custom-select:valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .25) +} + +.form-check-input.is-valid ~ .form-check-label, .was-validated .form-check-input:valid ~ .form-check-label { + color: #28a745 +} + +.form-check-input.is-valid ~ .valid-feedback, .form-check-input.is-valid ~ .valid-tooltip, .was-validated .form-check-input:valid ~ .valid-feedback, .was-validated .form-check-input:valid ~ .valid-tooltip { + display: block +} + +.custom-control-input.is-valid ~ .custom-control-label, .was-validated .custom-control-input:valid ~ .custom-control-label { + color: #28a745 +} + +.custom-control-input.is-valid ~ .custom-control-label::before, .was-validated .custom-control-input:valid ~ .custom-control-label::before { + border-color: #28a745 +} + +.custom-control-input.is-valid:checked ~ .custom-control-label::before, .was-validated .custom-control-input:valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57 +} + +.custom-control-input.is-valid:focus ~ .custom-control-label::before, .was-validated .custom-control-input:valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .25) +} + +.custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before, .was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745 +} + +.custom-file-input.is-valid ~ .custom-file-label, .was-validated .custom-file-input:valid ~ .custom-file-label { + border-color: #28a745 +} + +.custom-file-input.is-valid:focus ~ .custom-file-label, .was-validated .custom-file-input:valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .25) +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: .25rem; + font-size: 80%; + color: #dc3545 +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: .25rem .5rem; + margin-top: .1rem; + font-size: .875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, .9); + border-radius: .25rem +} + +.is-invalid ~ .invalid-feedback, .is-invalid ~ .invalid-tooltip, .was-validated :invalid ~ .invalid-feedback, .was-validated :invalid ~ .invalid-tooltip { + display: block +} + +.form-control.is-invalid, .was-validated .form-control:invalid { + border-color: #dc3545; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(.375em + .1875rem) center; + background-size: calc(.75em + .375rem) calc(.75em + .375rem) +} + +.form-control.is-invalid:focus, .was-validated .form-control:invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .25) +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem) +} + +.custom-select.is-invalid, .was-validated .custom-select:invalid { + border-color: #dc3545; + padding-right: calc(.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem) +} + +.custom-select.is-invalid:focus, .was-validated .custom-select:invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .25) +} + +.form-check-input.is-invalid ~ .form-check-label, .was-validated .form-check-input:invalid ~ .form-check-label { + color: #dc3545 +} + +.form-check-input.is-invalid ~ .invalid-feedback, .form-check-input.is-invalid ~ .invalid-tooltip, .was-validated .form-check-input:invalid ~ .invalid-feedback, .was-validated .form-check-input:invalid ~ .invalid-tooltip { + display: block +} + +.custom-control-input.is-invalid ~ .custom-control-label, .was-validated .custom-control-input:invalid ~ .custom-control-label { + color: #dc3545 +} + +.custom-control-input.is-invalid ~ .custom-control-label::before, .was-validated .custom-control-input:invalid ~ .custom-control-label::before { + border-color: #dc3545 +} + +.custom-control-input.is-invalid:checked ~ .custom-control-label::before, .was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d +} + +.custom-control-input.is-invalid:focus ~ .custom-control-label::before, .was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .25) +} + +.custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before, .was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545 +} + +.custom-file-input.is-invalid ~ .custom-file-label, .was-validated .custom-file-input:invalid ~ .custom-file-label { + border-color: #dc3545 +} + +.custom-file-input.is-invalid:focus ~ .custom-file-label, .was-validated .custom-file-input:invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .25) +} + +.form-inline { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center +} + +.form-inline .form-check { + width: 100% +} + +@media (min-width: 576px) { + .form-inline label { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + margin-bottom: 0 + } + + .form-inline .form-group { + display: -ms-flexbox; + display: flex; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -ms-flex-align: center; + align-items: center; + margin-bottom: 0 + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle + } + + .form-inline .form-control-plaintext { + display: inline-block + } + + .form-inline .custom-select, .form-inline .input-group { + width: auto + } + + .form-inline .form-check { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: auto; + padding-left: 0 + } + + .form-inline .form-check-input { + position: relative; + -ms-flex-negative: 0; + flex-shrink: 0; + margin-top: 0; + margin-right: .25rem; + margin-left: 0 + } + + .form-inline .custom-control { + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center + } + + .form-inline .custom-control-label { + margin-bottom: 0 + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: .375rem .75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: .25rem; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .btn { + transition: none + } +} + +.btn:hover { + color: #212529; + text-decoration: none +} + +.btn.focus, .btn:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.btn.disabled, .btn:disabled { + opacity: .65 +} + +a.btn.disabled, fieldset:disabled a.btn { + pointer-events: none +} + +.btn-primary { + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.btn-primary:hover { + color: #fff; + background-color: #0069d9; + border-color: #0062cc +} + +.btn-primary.focus, .btn-primary:focus { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; + box-shadow: 0 0 0 .2rem rgba(38, 143, 255, .5) +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.btn-primary:not(:disabled):not(.disabled).active, .btn-primary:not(:disabled):not(.disabled):active, .show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #0062cc; + border-color: #005cbf +} + +.btn-primary:not(:disabled):not(.disabled).active:focus, .btn-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(38, 143, 255, .5) +} + +.btn-secondary { + color: #fff; + background-color: #6c757d; + border-color: #6c757d +} + +.btn-secondary:hover { + color: #fff; + background-color: #5a6268; + border-color: #545b62 +} + +.btn-secondary.focus, .btn-secondary:focus { + color: #fff; + background-color: #5a6268; + border-color: #545b62; + box-shadow: 0 0 0 .2rem rgba(130, 138, 145, .5) +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #6c757d; + border-color: #6c757d +} + +.btn-secondary:not(:disabled):not(.disabled).active, .btn-secondary:not(:disabled):not(.disabled):active, .show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #545b62; + border-color: #4e555b + } + +.btn-secondary:not(:disabled):not(.disabled).active:focus, .btn-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(130, 138, 145, .5) +} + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745 +} + +.btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34 +} + +.btn-success.focus, .btn-success:focus { + color: #fff; + background-color: #218838; + border-color: #1e7e34; + box-shadow: 0 0 0 .2rem rgba(72, 180, 97, .5) +} + +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745 +} + +.btn-success:not(:disabled):not(.disabled).active, .btn-success:not(:disabled):not(.disabled):active, .show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430 +} + +.btn-success:not(:disabled):not(.disabled).active:focus, .btn-success:not(:disabled):not(.disabled):active:focus, .show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(72, 180, 97, .5) +} + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8 +} + +.btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b +} + +.btn-info.focus, .btn-info:focus { + color: #fff; + background-color: #138496; + border-color: #117a8b; + box-shadow: 0 0 0 .2rem rgba(58, 176, 195, .5) +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8 +} + +.btn-info:not(:disabled):not(.disabled).active, .btn-info:not(:disabled):not(.disabled):active, .show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f +} + +.btn-info:not(:disabled):not(.disabled).active:focus, .btn-info:not(:disabled):not(.disabled):active:focus, .show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(58, 176, 195, .5) +} + +.btn-warning { + color: #212529; + background-color: #ffc107; + border-color: #ffc107 +} + +.btn-warning:hover { + color: #212529; + background-color: #e0a800; + border-color: #d39e00 +} + +.btn-warning.focus, .btn-warning:focus { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; + box-shadow: 0 0 0 .2rem rgba(222, 170, 12, .5) +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffc107; + border-color: #ffc107 +} + +.btn-warning:not(:disabled):not(.disabled).active, .btn-warning:not(:disabled):not(.disabled):active, .show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #d39e00; + border-color: #c69500 +} + +.btn-warning:not(:disabled):not(.disabled).active:focus, .btn-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(222, 170, 12, .5) +} + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545 +} + +.btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130 +} + +.btn-danger.focus, .btn-danger:focus { + color: #fff; + background-color: #c82333; + border-color: #bd2130; + box-shadow: 0 0 0 .2rem rgba(225, 83, 97, .5) +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545 +} + +.btn-danger:not(:disabled):not(.disabled).active, .btn-danger:not(:disabled):not(.disabled):active, .show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d +} + +.btn-danger:not(:disabled):not(.disabled).active:focus, .btn-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(225, 83, 97, .5) +} + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa +} + +.btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5 +} + +.btn-light.focus, .btn-light:focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 0 0 .2rem rgba(216, 217, 219, .5) +} + +.btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa +} + +.btn-light:not(:disabled):not(.disabled).active, .btn-light:not(:disabled):not(.disabled):active, .show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df +} + +.btn-light:not(:disabled):not(.disabled).active:focus, .btn-light:not(:disabled):not(.disabled):active:focus, .show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(216, 217, 219, .5) +} + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40 +} + +.btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124 +} + +.btn-dark.focus, .btn-dark:focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; + box-shadow: 0 0 0 .2rem rgba(82, 88, 93, .5) +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40 +} + +.btn-dark:not(:disabled):not(.disabled).active, .btn-dark:not(:disabled):not(.disabled):active, .show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d +} + +.btn-dark:not(:disabled):not(.disabled).active:focus, .btn-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(82, 88, 93, .5) +} + +.btn-outline-primary { + color: #007bff; + border-color: #007bff +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.btn-outline-primary.focus, .btn-outline-primary:focus { + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .5) +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #007bff; + background-color: transparent +} + +.btn-outline-primary:not(:disabled):not(.disabled).active, .btn-outline-primary:not(:disabled):not(.disabled):active, .show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.btn-outline-primary:not(:disabled):not(.disabled).active:focus, .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .5) +} + +.btn-outline-secondary { + color: #6c757d; + border-color: #6c757d +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #6c757d; + border-color: #6c757d +} + +.btn-outline-secondary.focus, .btn-outline-secondary:focus { + box-shadow: 0 0 0 .2rem rgba(108, 117, 125, .5) +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #6c757d; + background-color: transparent +} + +.btn-outline-secondary:not(:disabled):not(.disabled).active, .btn-outline-secondary:not(:disabled):not(.disabled):active, .show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d +} + +.btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(108, 117, 125, .5) +} + +.btn-outline-success { + color: #28a745; + border-color: #28a745 +} + +.btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745 +} + +.btn-outline-success.focus, .btn-outline-success:focus { + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .5) +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent +} + +.btn-outline-success:not(:disabled):not(.disabled).active, .btn-outline-success:not(:disabled):not(.disabled):active, .show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745 +} + +.btn-outline-success:not(:disabled):not(.disabled).active:focus, .btn-outline-success:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .5) +} + +.btn-outline-info { + color: #17a2b8; + border-color: #17a2b8 +} + +.btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8 +} + +.btn-outline-info.focus, .btn-outline-info:focus { + box-shadow: 0 0 0 .2rem rgba(23, 162, 184, .5) +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent +} + +.btn-outline-info:not(:disabled):not(.disabled).active, .btn-outline-info:not(:disabled):not(.disabled):active, .show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8 +} + +.btn-outline-info:not(:disabled):not(.disabled).active:focus, .btn-outline-info:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(23, 162, 184, .5) +} + +.btn-outline-warning { + color: #ffc107; + border-color: #ffc107 +} + +.btn-outline-warning:hover { + color: #212529; + background-color: #ffc107; + border-color: #ffc107 +} + +.btn-outline-warning.focus, .btn-outline-warning:focus { + box-shadow: 0 0 0 .2rem rgba(255, 193, 7, .5) +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffc107; + background-color: transparent +} + +.btn-outline-warning:not(:disabled):not(.disabled).active, .btn-outline-warning:not(:disabled):not(.disabled):active, .show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffc107; + border-color: #ffc107 +} + +.btn-outline-warning:not(:disabled):not(.disabled).active:focus, .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(255, 193, 7, .5) +} + +.btn-outline-danger { + color: #dc3545; + border-color: #dc3545 +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545 +} + +.btn-outline-danger.focus, .btn-outline-danger:focus { + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .5) +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent +} + +.btn-outline-danger:not(:disabled):not(.disabled).active, .btn-outline-danger:not(:disabled):not(.disabled):active, .show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545 +} + +.btn-outline-danger:not(:disabled):not(.disabled).active:focus, .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .5) +} + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa +} + +.btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa +} + +.btn-outline-light.focus, .btn-outline-light:focus { + box-shadow: 0 0 0 .2rem rgba(248, 249, 250, .5) +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent +} + +.btn-outline-light:not(:disabled):not(.disabled).active, .btn-outline-light:not(:disabled):not(.disabled):active, .show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa +} + +.btn-outline-light:not(:disabled):not(.disabled).active:focus, .btn-outline-light:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(248, 249, 250, .5) +} + +.btn-outline-dark { + color: #343a40; + border-color: #343a40 +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40 +} + +.btn-outline-dark.focus, .btn-outline-dark:focus { + box-shadow: 0 0 0 .2rem rgba(52, 58, 64, .5) +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent +} + +.btn-outline-dark:not(:disabled):not(.disabled).active, .btn-outline-dark:not(:disabled):not(.disabled):active, .show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40 +} + +.btn-outline-dark:not(:disabled):not(.disabled).active:focus, .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 .2rem rgba(52, 58, 64, .5) +} + +.btn-link { + font-weight: 400; + color: #007bff; + text-decoration: none +} + +.btn-link:hover { + color: #0056b3; + text-decoration: underline +} + +.btn-link.focus, .btn-link:focus { + text-decoration: underline; + box-shadow: none +} + +.btn-link.disabled, .btn-link:disabled { + color: #6c757d; + pointer-events: none +} + +.btn-group-lg > .btn, .btn-lg { + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +.btn-group-sm > .btn, .btn-sm { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.btn-block { + display: block; + width: 100% +} + +.btn-block + .btn-block { + margin-top: .5rem +} + +input[type=button].btn-block, input[type=reset].btn-block, input[type=submit].btn-block { + width: 100% +} + +.fade { + transition: opacity .15s linear +} + +@media (prefers-reduced-motion: reduce) { + .fade { + transition: none + } +} + +.fade:not(.show) { + opacity: 0 +} + +.collapse:not(.show) { + display: none +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height .35s ease +} + +@media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none + } +} + +.dropdown, .dropleft, .dropright, .dropup { + position: relative +} + +.dropdown-toggle { + white-space: nowrap +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid; + border-right: .3em solid transparent; + border-bottom: 0; + border-left: .3em solid transparent +} + +.dropdown-toggle:empty::after { + margin-left: 0 +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: .5rem 0; + margin: .125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: .25rem +} + +.dropdown-menu-left { + right: auto; + left: 0 +} + +.dropdown-menu-right { + right: 0; + left: auto +} + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0 + } + + .dropdown-menu-sm-right { + right: 0; + left: auto + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0 + } + + .dropdown-menu-md-right { + right: 0; + left: auto + } +} + +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0 + } + + .dropdown-menu-lg-right { + right: 0; + left: auto + } +} + +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0 + } + + .dropdown-menu-xl-right { + right: 0; + left: auto + } +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: .125rem +} + +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: 0; + border-right: .3em solid transparent; + border-bottom: .3em solid; + border-left: .3em solid transparent +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0 +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: .125rem +} + +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid transparent; + border-right: 0; + border-bottom: .3em solid transparent; + border-left: .3em solid +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0 +} + +.dropright .dropdown-toggle::after { + vertical-align: 0 +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: .125rem +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: .255em; + vertical-align: .255em; + content: "" +} + +.dropleft .dropdown-toggle::after { + display: none +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid transparent; + border-right: .3em solid; + border-bottom: .3em solid transparent +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0 +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0 +} + +.dropdown-menu[x-placement^=bottom], .dropdown-menu[x-placement^=left], .dropdown-menu[x-placement^=right], .dropdown-menu[x-placement^=top] { + right: auto; + bottom: auto +} + +.dropdown-divider { + height: 0; + margin: .5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef +} + +.dropdown-item { + display: block; + width: 100%; + padding: .25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0 +} + +.dropdown-item:focus, .dropdown-item:hover { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent +} + +.dropdown-menu.show { + display: block +} + +.dropdown-header { + display: block; + padding: .5rem 1.5rem; + margin-bottom: 0; + font-size: .875rem; + color: #6c757d; + white-space: nowrap +} + +.dropdown-item-text { + display: block; + padding: .25rem 1.5rem; + color: #212529 +} + +.btn-group, .btn-group-vertical { + position: relative; + display: -ms-inline-flexbox; + display: inline-flex; + vertical-align: middle +} + +.btn-group-vertical > .btn, .btn-group > .btn { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto +} + +.btn-group-vertical > .btn:hover, .btn-group > .btn:hover { + z-index: 1 +} + +.btn-group-vertical > .btn.active, .btn-group-vertical > .btn:active, .btn-group-vertical > .btn:focus, .btn-group > .btn.active, .btn-group > .btn:active, .btn-group > .btn:focus { + z-index: 1 +} + +.btn-toolbar { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-pack: start; + justify-content: flex-start +} + +.btn-toolbar .input-group { + width: auto +} + +.btn-group > .btn-group:not(:first-child), .btn-group > .btn:not(:first-child) { + margin-left: -1px +} + +.btn-group > .btn-group:not(:last-child) > .btn, .btn-group > .btn:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.btn-group > .btn-group:not(:first-child) > .btn, .btn-group > .btn:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.dropdown-toggle-split { + padding-right: .5625rem; + padding-left: .5625rem +} + +.dropdown-toggle-split::after, .dropright .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after { + margin-left: 0 +} + +.dropleft .dropdown-toggle-split::before { + margin-right: 0 +} + +.btn-group-sm > .btn + .dropdown-toggle-split, .btn-sm + .dropdown-toggle-split { + padding-right: .375rem; + padding-left: .375rem +} + +.btn-group-lg > .btn + .dropdown-toggle-split, .btn-lg + .dropdown-toggle-split { + padding-right: .75rem; + padding-left: .75rem +} + +.btn-group-vertical { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: center; + justify-content: center +} + +.btn-group-vertical > .btn, .btn-group-vertical > .btn-group { + width: 100% +} + +.btn-group-vertical > .btn-group:not(:first-child), .btn-group-vertical > .btn:not(:first-child) { + margin-top: -1px +} + +.btn-group-vertical > .btn-group:not(:last-child) > .btn, .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.btn-group-vertical > .btn-group:not(:first-child) > .btn, .btn-group-vertical > .btn:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.btn-group-toggle > .btn, .btn-group-toggle > .btn-group > .btn { + margin-bottom: 0 +} + +.btn-group-toggle > .btn input[type=checkbox], .btn-group-toggle > .btn input[type=radio], .btn-group-toggle > .btn-group > .btn input[type=checkbox], .btn-group-toggle > .btn-group > .btn input[type=radio] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none +} + +.input-group { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: stretch; + align-items: stretch; + width: 100% +} + +.input-group > .custom-file, .input-group > .custom-select, .input-group > .form-control, .input-group > .form-control-plaintext { + position: relative; + -ms-flex: 1 1 0%; + flex: 1 1 0%; + min-width: 0; + margin-bottom: 0 +} + +.input-group > .custom-file + .custom-file, .input-group > .custom-file + .custom-select, .input-group > .custom-file + .form-control, .input-group > .custom-select + .custom-file, .input-group > .custom-select + .custom-select, .input-group > .custom-select + .form-control, .input-group > .form-control + .custom-file, .input-group > .form-control + .custom-select, .input-group > .form-control + .form-control, .input-group > .form-control-plaintext + .custom-file, .input-group > .form-control-plaintext + .custom-select, .input-group > .form-control-plaintext + .form-control { + margin-left: -1px +} + +.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label, .input-group > .custom-select:focus, .input-group > .form-control:focus { + z-index: 3 +} + +.input-group > .custom-file .custom-file-input:focus { + z-index: 4 +} + +.input-group > .custom-select:not(:last-child), .input-group > .form-control:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .custom-select:not(:first-child), .input-group > .form-control:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group > .custom-file { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.input-group-append, .input-group-prepend { + display: -ms-flexbox; + display: flex +} + +.input-group-append .btn, .input-group-prepend .btn { + position: relative; + z-index: 2 +} + +.input-group-append .btn:focus, .input-group-prepend .btn:focus { + z-index: 3 +} + +.input-group-append .btn + .btn, .input-group-append .btn + .input-group-text, .input-group-append .input-group-text + .btn, .input-group-append .input-group-text + .input-group-text, .input-group-prepend .btn + .btn, .input-group-prepend .btn + .input-group-text, .input-group-prepend .input-group-text + .btn, .input-group-prepend .input-group-text + .input-group-text { + margin-left: -1px +} + +.input-group-prepend { + margin-right: -1px +} + +.input-group-append { + margin-left: -1px +} + +.input-group-text { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: .375rem .75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: .25rem +} + +.input-group-text input[type=checkbox], .input-group-text input[type=radio] { + margin-top: 0 +} + +.input-group-lg > .custom-select, .input-group-lg > .form-control:not(textarea) { + height: calc(1.5em + 1rem + 2px) +} + +.input-group-lg > .custom-select, .input-group-lg > .form-control, .input-group-lg > .input-group-append > .btn, .input-group-lg > .input-group-append > .input-group-text, .input-group-lg > .input-group-prepend > .btn, .input-group-lg > .input-group-prepend > .input-group-text { + padding: .5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: .3rem +} + +.input-group-sm > .custom-select, .input-group-sm > .form-control:not(textarea) { + height: calc(1.5em + .5rem + 2px) +} + +.input-group-sm > .custom-select, .input-group-sm > .form-control, .input-group-sm > .input-group-append > .btn, .input-group-sm > .input-group-append > .input-group-text, .input-group-sm > .input-group-prepend > .btn, .input-group-sm > .input-group-prepend > .input-group-text { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5; + border-radius: .2rem +} + +.input-group-lg > .custom-select, .input-group-sm > .custom-select { + padding-right: 1.75rem +} + +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), .input-group > .input-group-append:last-child > .input-group-text:not(:last-child), .input-group > .input-group-append:not(:last-child) > .btn, .input-group > .input-group-append:not(:last-child) > .input-group-text, .input-group > .input-group-prepend > .btn, .input-group > .input-group-prepend > .input-group-text { + border-top-right-radius: 0; + border-bottom-right-radius: 0 +} + +.input-group > .input-group-append > .btn, .input-group > .input-group-append > .input-group-text, .input-group > .input-group-prepend:first-child > .btn:not(:first-child), .input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child), .input-group > .input-group-prepend:not(:first-child) > .btn, .input-group > .input-group-prepend:not(:first-child) > .input-group-text { + border-top-left-radius: 0; + border-bottom-left-radius: 0 +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem +} + +.custom-control-inline { + display: -ms-inline-flexbox; + display: inline-flex; + margin-right: 1rem +} + +.custom-control-input { + position: absolute; + left: 0; + z-index: -1; + width: 1rem; + height: 1.25rem; + opacity: 0 +} + +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff +} + +.custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #80bdff +} + +.custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff +} + +.custom-control-input:disabled ~ .custom-control-label, .custom-control-input[disabled] ~ .custom-control-label { + color: #6c757d +} + +.custom-control-input:disabled ~ .custom-control-label::before, .custom-control-input[disabled] ~ .custom-control-label::before { + background-color: #e9ecef +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top +} + +.custom-control-label::before { + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px +} + +.custom-control-label::after { + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50%/50% 50% +} + +.custom-checkbox .custom-control-label::before { + border-radius: .25rem +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e") +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #007bff; + background-color: #007bff +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e") +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, .5) +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, .5) +} + +.custom-radio .custom-control-label::before { + border-radius: 50% +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e") +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, .5) +} + +.custom-switch { + padding-left: 2.25rem +} + +.custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: .5rem +} + +.custom-switch .custom-control-label::after { + top: calc(.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: .5rem; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, -webkit-transform .15s ease-in-out; + transition: transform .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + transition: transform .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out, -webkit-transform .15s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + -webkit-transform: translateX(.75rem); + transform: translateX(.75rem) +} + +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, .5) +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + padding: .375rem 1.75rem .375rem .75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px; + border: 1px solid #ced4da; + border-radius: .25rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-select:focus::-ms-value { + color: #495057; + background-color: #fff +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: .75rem; + background-image: none +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef +} + +.custom-select::-ms-expand { + display: none +} + +.custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057 +} + +.custom-select-sm { + height: calc(1.5em + .5rem + 2px); + padding-top: .25rem; + padding-bottom: .25rem; + padding-left: .5rem; + font-size: .875rem +} + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: .5rem; + padding-bottom: .5rem; + padding-left: 1rem; + font-size: 1.25rem +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + margin-bottom: 0 +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + .75rem + 2px); + margin: 0; + opacity: 0 +} + +.custom-file-input:focus ~ .custom-file-label { + border-color: #80bdff; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-file-input:disabled ~ .custom-file-label, .custom-file-input[disabled] ~ .custom-file-label { + background-color: #e9ecef +} + +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse" +} + +.custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse) +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + .75rem + 2px); + padding: .375rem .75rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: .25rem +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + .75rem); + padding: .375rem .75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 .25rem .25rem 0 +} + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.custom-range:focus { + outline: 0 +} + +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.custom-range::-moz-focus-outer { + border: 0 +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -.25rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + -webkit-appearance: none; + appearance: none +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #b3d7ff +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -moz-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + -moz-appearance: none; + appearance: none +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + -moz-transition: none; + transition: none + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #b3d7ff +} + +.custom-range::-moz-range-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: .2rem; + margin-left: .2rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + -ms-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + appearance: none +} + +@media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + -ms-transition: none; + transition: none + } +} + +.custom-range::-ms-thumb:active { + background-color: #b3d7ff +} + +.custom-range::-ms-track { + width: 100%; + height: .5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: .5rem +} + +.custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem +} + +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem +} + +.custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd +} + +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default +} + +.custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd +} + +.custom-range:disabled::-moz-range-track { + cursor: default +} + +.custom-range:disabled::-ms-thumb { + background-color: #adb5bd +} + +.custom-control-label::before, .custom-file-label, .custom-select { + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .custom-control-label::before, .custom-file-label, .custom-select { + transition: none + } +} + +.nav { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.nav-link { + display: block; + padding: .5rem 1rem +} + +.nav-link:focus, .nav-link:hover { + text-decoration: none +} + +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default +} + +.nav-tabs { + border-bottom: 1px solid #dee2e6 +} + +.nav-tabs .nav-item { + margin-bottom: -1px +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { + border-color: #e9ecef #e9ecef #dee2e6 +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent +} + +.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.nav-pills .nav-link { + border-radius: .25rem +} + +.nav-pills .nav-link.active, .nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff +} + +.nav-fill .nav-item { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + text-align: center +} + +.nav-justified .nav-item { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -ms-flex-positive: 1; + flex-grow: 1; + text-align: center +} + +.tab-content > .tab-pane { + display: none +} + +.tab-content > .active { + display: block +} + +.navbar { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; + padding: .5rem 1rem +} + +.navbar .container, .navbar .container-fluid, .navbar .container-lg, .navbar .container-md, .navbar .container-sm, .navbar .container-xl { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between +} + +.navbar-brand { + display: inline-block; + padding-top: .3125rem; + padding-bottom: .3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap +} + +.navbar-brand:focus, .navbar-brand:hover { + text-decoration: none +} + +.navbar-nav { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0 +} + +.navbar-nav .dropdown-menu { + position: static; + float: none +} + +.navbar-text { + display: inline-block; + padding-top: .5rem; + padding-bottom: .5rem +} + +.navbar-collapse { + -ms-flex-preferred-size: 100%; + flex-basis: 100%; + -ms-flex-positive: 1; + flex-grow: 1; + -ms-flex-align: center; + align-items: center +} + +.navbar-toggler { + padding: .25rem .75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: .25rem +} + +.navbar-toggler:focus, .navbar-toggler:hover { + text-decoration: none +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100% +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-xl { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start + } + + .navbar-expand-sm .navbar-nav { + -ms-flex-direction: row; + flex-direction: row + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-sm > .container, .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap + } + + .navbar-expand-sm .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto + } + + .navbar-expand-sm .navbar-toggler { + display: none + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-md, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-xl { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start + } + + .navbar-expand-md .navbar-nav { + -ms-flex-direction: row; + flex-direction: row + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-md > .container, .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-md, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap + } + + .navbar-expand-md .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto + } + + .navbar-expand-md .navbar-toggler { + display: none + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-xl { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start + } + + .navbar-expand-lg .navbar-nav { + -ms-flex-direction: row; + flex-direction: row + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-lg > .container, .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap + } + + .navbar-expand-lg .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto + } + + .navbar-expand-lg .navbar-toggler { + display: none + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-xl { + padding-right: 0; + padding-left: 0 + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start + } + + .navbar-expand-xl .navbar-nav { + -ms-flex-direction: row; + flex-direction: row + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem + } + + .navbar-expand-xl > .container, .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap + } + + .navbar-expand-xl .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto + } + + .navbar-expand-xl .navbar-toggler { + display: none + } +} + +.navbar-expand { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start +} + +.navbar-expand > .container, .navbar-expand > .container-fluid, .navbar-expand > .container-lg, .navbar-expand > .container-md, .navbar-expand > .container-sm, .navbar-expand > .container-xl { + padding-right: 0; + padding-left: 0 +} + +.navbar-expand .navbar-nav { + -ms-flex-direction: row; + flex-direction: row +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem +} + +.navbar-expand > .container, .navbar-expand > .container-fluid, .navbar-expand > .container-lg, .navbar-expand > .container-md, .navbar-expand > .container-sm, .navbar-expand > .container-xl { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap +} + +.navbar-expand .navbar-collapse { + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto +} + +.navbar-expand .navbar-toggler { + display: none +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, .9) +} + +.navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover { + color: rgba(0, 0, 0, .9) +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, .5) +} + +.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover { + color: rgba(0, 0, 0, .7) +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, .3) +} + +.navbar-light .navbar-nav .active > .nav-link, .navbar-light .navbar-nav .nav-link.active, .navbar-light .navbar-nav .nav-link.show, .navbar-light .navbar-nav .show > .nav-link { + color: rgba(0, 0, 0, .9) +} + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, .5); + border-color: rgba(0, 0, 0, .1) +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") +} + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, .5) +} + +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, .9) +} + +.navbar-light .navbar-text a:focus, .navbar-light .navbar-text a:hover { + color: rgba(0, 0, 0, .9) +} + +.navbar-dark .navbar-brand { + color: #fff +} + +.navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover { + color: #fff +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, .5) +} + +.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover { + color: rgba(255, 255, 255, .75) +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, .25) +} + +.navbar-dark .navbar-nav .active > .nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show > .nav-link { + color: #fff +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, .5); + border-color: rgba(255, 255, 255, .1) +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, .5) +} + +.navbar-dark .navbar-text a { + color: #fff +} + +.navbar-dark .navbar-text a:focus, .navbar-dark .navbar-text a:hover { + color: #fff +} + +.card { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, .125); + border-radius: .25rem +} + +.card > hr { + margin-right: 0; + margin-left: 0 +} + +.card > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.card-body { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 1px; + padding: 1.25rem +} + +.card-title { + margin-bottom: .75rem +} + +.card-subtitle { + margin-top: -.375rem; + margin-bottom: 0 +} + +.card-text:last-child { + margin-bottom: 0 +} + +.card-link:hover { + text-decoration: none +} + +.card-link + .card-link { + margin-left: 1.25rem +} + +.card-header { + padding: .75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, .03); + border-bottom: 1px solid rgba(0, 0, 0, .125) +} + +.card-header:first-child { + border-radius: calc(.25rem - 1px) calc(.25rem - 1px) 0 0 +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0 +} + +.card-footer { + padding: .75rem 1.25rem; + background-color: rgba(0, 0, 0, .03); + border-top: 1px solid rgba(0, 0, 0, .125) +} + +.card-footer:last-child { + border-radius: 0 0 calc(.25rem - 1px) calc(.25rem - 1px) +} + +.card-header-tabs { + margin-right: -.625rem; + margin-bottom: -.75rem; + margin-left: -.625rem; + border-bottom: 0 +} + +.card-header-pills { + margin-right: -.625rem; + margin-left: -.625rem +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem +} + +.card-img, .card-img-bottom, .card-img-top { + -ms-flex-negative: 0; + flex-shrink: 0; + width: 100% +} + +.card-img, .card-img-top { + border-top-left-radius: calc(.25rem - 1px); + border-top-right-radius: calc(.25rem - 1px) +} + +.card-img, .card-img-bottom { + border-bottom-right-radius: calc(.25rem - 1px); + border-bottom-left-radius: calc(.25rem - 1px) +} + +.card-deck .card { + margin-bottom: 15px +} + +@media (min-width: 576px) { + .card-deck { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px + } + + .card-deck .card { + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px + } +} + +.card-group > .card { + margin-bottom: 15px +} + +@media (min-width: 576px) { + .card-group { + display: -ms-flexbox; + display: flex; + -ms-flex-flow: row wrap; + flex-flow: row wrap + } + + .card-group > .card { + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-bottom: 0 + } + + .card-group > .card + .card { + margin-left: 0; + border-left: 0 + } + + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0 + } + + .card-group > .card:not(:last-child) .card-header, .card-group > .card:not(:last-child) .card-img-top { + border-top-right-radius: 0 + } + + .card-group > .card:not(:last-child) .card-footer, .card-group > .card:not(:last-child) .card-img-bottom { + border-bottom-right-radius: 0 + } + + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0 + } + + .card-group > .card:not(:first-child) .card-header, .card-group > .card:not(:first-child) .card-img-top { + border-top-left-radius: 0 + } + + .card-group > .card:not(:first-child) .card-footer, .card-group > .card:not(:first-child) .card-img-bottom { + border-bottom-left-radius: 0 + } +} + +.card-columns .card { + margin-bottom: .75rem +} + +@media (min-width: 576px) { + .card-columns { + -webkit-column-count: 3; + -moz-column-count: 3; + column-count: 3; + -webkit-column-gap: 1.25rem; + -moz-column-gap: 1.25rem; + column-gap: 1.25rem; + orphans: 1; + widows: 1 + } + + .card-columns .card { + display: inline-block; + width: 100% + } +} + +.accordion > .card { + overflow: hidden +} + +.accordion > .card:not(:last-of-type) { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0 +} + +.accordion > .card:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.accordion > .card > .card-header { + border-radius: 0; + margin-bottom: -1px +} + +.breadcrumb { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: .75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: .25rem +} + +.breadcrumb-item + .breadcrumb-item { + padding-left: .5rem +} + +.breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: .5rem; + color: #6c757d; + content: "/" +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none +} + +.breadcrumb-item.active { + color: #6c757d +} + +.pagination { + display: -ms-flexbox; + display: flex; + padding-left: 0; + list-style: none; + border-radius: .25rem +} + +.page-link { + position: relative; + display: block; + padding: .5rem .75rem; + margin-left: -1px; + line-height: 1.25; + color: #007bff; + background-color: #fff; + border: 1px solid #dee2e6 +} + +.page-link:hover { + z-index: 2; + color: #0056b3; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6 +} + +.page-link:focus { + z-index: 3; + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .25) +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.page-item:last-child .page-link { + border-top-right-radius: .25rem; + border-bottom-right-radius: .25rem +} + +.page-item.active .page-link { + z-index: 3; + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6 +} + +.pagination-lg .page-link { + padding: .75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5 +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: .3rem; + border-bottom-left-radius: .3rem +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: .3rem; + border-bottom-right-radius: .3rem +} + +.pagination-sm .page-link { + padding: .25rem .5rem; + font-size: .875rem; + line-height: 1.5 +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: .2rem; + border-bottom-left-radius: .2rem +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: .2rem; + border-bottom-right-radius: .2rem +} + +.badge { + display: inline-block; + padding: .25em .4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25rem; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .badge { + transition: none + } +} + +a.badge:focus, a.badge:hover { + text-decoration: none +} + +.badge:empty { + display: none +} + +.btn .badge { + position: relative; + top: -1px +} + +.badge-pill { + padding-right: .6em; + padding-left: .6em; + border-radius: 10rem +} + +.badge-primary { + color: #fff; + background-color: #007bff +} + +a.badge-primary:focus, a.badge-primary:hover { + color: #fff; + background-color: #0062cc +} + +a.badge-primary.focus, a.badge-primary:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(0, 123, 255, .5) +} + +.badge-secondary { + color: #fff; + background-color: #6c757d +} + +a.badge-secondary:focus, a.badge-secondary:hover { + color: #fff; + background-color: #545b62 +} + +a.badge-secondary.focus, a.badge-secondary:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(108, 117, 125, .5) +} + +.badge-success { + color: #fff; + background-color: #28a745 +} + +a.badge-success:focus, a.badge-success:hover { + color: #fff; + background-color: #1e7e34 +} + +a.badge-success.focus, a.badge-success:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .5) +} + +.badge-info { + color: #fff; + background-color: #17a2b8 +} + +a.badge-info:focus, a.badge-info:hover { + color: #fff; + background-color: #117a8b +} + +a.badge-info.focus, a.badge-info:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(23, 162, 184, .5) +} + +.badge-warning { + color: #212529; + background-color: #ffc107 +} + +a.badge-warning:focus, a.badge-warning:hover { + color: #212529; + background-color: #d39e00 +} + +a.badge-warning.focus, a.badge-warning:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(255, 193, 7, .5) +} + +.badge-danger { + color: #fff; + background-color: #dc3545 +} + +a.badge-danger:focus, a.badge-danger:hover { + color: #fff; + background-color: #bd2130 +} + +a.badge-danger.focus, a.badge-danger:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(220, 53, 69, .5) +} + +.badge-light { + color: #212529; + background-color: #f8f9fa +} + +a.badge-light:focus, a.badge-light:hover { + color: #212529; + background-color: #dae0e5 +} + +a.badge-light.focus, a.badge-light:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(248, 249, 250, .5) +} + +.badge-dark { + color: #fff; + background-color: #343a40 +} + +a.badge-dark:focus, a.badge-dark:hover { + color: #fff; + background-color: #1d2124 +} + +a.badge-dark.focus, a.badge-dark:focus { + outline: 0; + box-shadow: 0 0 0 .2rem rgba(52, 58, 64, .5) +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: .3rem +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0 +} + +.alert { + position: relative; + padding: .75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: .25rem +} + +.alert-heading { + color: inherit +} + +.alert-link { + font-weight: 700 +} + +.alert-dismissible { + padding-right: 4rem +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: .75rem 1.25rem; + color: inherit +} + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff +} + +.alert-primary hr { + border-top-color: #9fcdff +} + +.alert-primary .alert-link { + color: #002752 +} + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db +} + +.alert-secondary hr { + border-top-color: #c8cbcf +} + +.alert-secondary .alert-link { + color: #202326 +} + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb +} + +.alert-success hr { + border-top-color: #b1dfbb +} + +.alert-success .alert-link { + color: #0b2e13 +} + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb +} + +.alert-info hr { + border-top-color: #abdde5 +} + +.alert-info .alert-link { + color: #062c33 +} + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba +} + +.alert-warning hr { + border-top-color: #ffe8a1 +} + +.alert-warning .alert-link { + color: #533f03 +} + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb +} + +.alert-danger hr { + border-top-color: #f1b0b7 +} + +.alert-danger .alert-link { + color: #491217 +} + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe +} + +.alert-light hr { + border-top-color: #ececf6 +} + +.alert-light .alert-link { + color: #686868 +} + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca +} + +.alert-dark hr { + border-top-color: #b9bbbe +} + +.alert-dark .alert-link { + color: #040505 +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 1rem 0 + } + to { + background-position: 0 0 + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0 + } + to { + background-position: 0 0 + } +} + +.progress { + display: -ms-flexbox; + display: flex; + height: 1rem; + overflow: hidden; + font-size: .75rem; + background-color: #e9ecef; + border-radius: .25rem +} + +.progress-bar { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + overflow: hidden; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width .6s ease +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem +} + +.progress-bar-animated { + -webkit-animation: progress-bar-stripes 1s linear infinite; + animation: progress-bar-stripes 1s linear infinite +} + +@media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + -webkit-animation: none; + animation: none + } +} + +.media { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start +} + +.media-body { + -ms-flex: 1; + flex: 1 +} + +.list-group { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0 +} + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit +} + +.list-group-item-action:focus, .list-group-item-action:hover { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa +} + +.list-group-item-action:active { + color: #212529; + background-color: #e9ecef +} + +.list-group-item { + position: relative; + display: block; + padding: .75rem 1.25rem; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, .125) +} + +.list-group-item:first-child { + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.list-group-item:last-child { + border-bottom-right-radius: .25rem; + border-bottom-left-radius: .25rem +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff +} + +.list-group-item + .list-group-item { + border-top-width: 0 +} + +.list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px +} + +.list-group-horizontal { + -ms-flex-direction: row; + flex-direction: row +} + +.list-group-horizontal .list-group-item:first-child { + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 +} + +.list-group-horizontal .list-group-item:last-child { + border-top-right-radius: .25rem; + border-bottom-left-radius: 0 +} + +.list-group-horizontal .list-group-item.active { + margin-top: 0 +} + +.list-group-horizontal .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0 +} + +.list-group-horizontal .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + -ms-flex-direction: row; + flex-direction: row + } + + .list-group-horizontal-sm .list-group-item:first-child { + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-sm .list-group-item:last-child { + border-top-right-radius: .25rem; + border-bottom-left-radius: 0 + } + + .list-group-horizontal-sm .list-group-item.active { + margin-top: 0 + } + + .list-group-horizontal-sm .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0 + } + + .list-group-horizontal-sm .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px + } +} + +@media (min-width: 768px) { + .list-group-horizontal-md { + -ms-flex-direction: row; + flex-direction: row + } + + .list-group-horizontal-md .list-group-item:first-child { + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-md .list-group-item:last-child { + border-top-right-radius: .25rem; + border-bottom-left-radius: 0 + } + + .list-group-horizontal-md .list-group-item.active { + margin-top: 0 + } + + .list-group-horizontal-md .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0 + } + + .list-group-horizontal-md .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px + } +} + +@media (min-width: 992px) { + .list-group-horizontal-lg { + -ms-flex-direction: row; + flex-direction: row + } + + .list-group-horizontal-lg .list-group-item:first-child { + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-lg .list-group-item:last-child { + border-top-right-radius: .25rem; + border-bottom-left-radius: 0 + } + + .list-group-horizontal-lg .list-group-item.active { + margin-top: 0 + } + + .list-group-horizontal-lg .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0 + } + + .list-group-horizontal-lg .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px + } +} + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + -ms-flex-direction: row; + flex-direction: row + } + + .list-group-horizontal-xl .list-group-item:first-child { + border-bottom-left-radius: .25rem; + border-top-right-radius: 0 + } + + .list-group-horizontal-xl .list-group-item:last-child { + border-top-right-radius: .25rem; + border-bottom-left-radius: 0 + } + + .list-group-horizontal-xl .list-group-item.active { + margin-top: 0 + } + + .list-group-horizontal-xl .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0 + } + + .list-group-horizontal-xl .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px + } +} + +.list-group-flush .list-group-item { + border-right-width: 0; + border-left-width: 0; + border-radius: 0 +} + +.list-group-flush .list-group-item:first-child { + border-top-width: 0 +} + +.list-group-flush:last-child .list-group-item:last-child { + border-bottom-width: 0 +} + +.list-group-item-primary { + color: #004085; + background-color: #b8daff +} + +.list-group-item-primary.list-group-item-action:focus, .list-group-item-primary.list-group-item-action:hover { + color: #004085; + background-color: #9fcdff +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #004085; + border-color: #004085 +} + +.list-group-item-secondary { + color: #383d41; + background-color: #d6d8db +} + +.list-group-item-secondary.list-group-item-action:focus, .list-group-item-secondary.list-group-item-action:hover { + color: #383d41; + background-color: #c8cbcf +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #383d41; + border-color: #383d41 +} + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb +} + +.list-group-item-success.list-group-item-action:focus, .list-group-item-success.list-group-item-action:hover { + color: #155724; + background-color: #b1dfbb +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724 +} + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb +} + +.list-group-item-info.list-group-item-action:focus, .list-group-item-info.list-group-item-action:hover { + color: #0c5460; + background-color: #abdde5 +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460 +} + +.list-group-item-warning { + color: #856404; + background-color: #ffeeba +} + +.list-group-item-warning.list-group-item-action:focus, .list-group-item-warning.list-group-item-action:hover { + color: #856404; + background-color: #ffe8a1 +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856404; + border-color: #856404 +} + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb +} + +.list-group-item-danger.list-group-item-action:focus, .list-group-item-danger.list-group-item-action:hover { + color: #721c24; + background-color: #f1b0b7 +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24 +} + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe +} + +.list-group-item-light.list-group-item-action:focus, .list-group-item-light.list-group-item-action:hover { + color: #818182; + background-color: #ececf6 +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182 +} + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca +} + +.list-group-item-dark.list-group-item-action:focus, .list-group-item-dark.list-group-item-action:hover { + color: #1b1e21; + background-color: #b9bbbe +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21 +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5 +} + +.close:hover { + color: #000; + text-decoration: none +} + +.close:not(:disabled):not(.disabled):focus, .close:not(:disabled):not(.disabled):hover { + opacity: .75 +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +a.close.disabled { + pointer-events: none +} + +.toast { + max-width: 350px; + overflow: hidden; + font-size: .875rem; + background-color: rgba(255, 255, 255, .85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .1); + box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .1); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + opacity: 0; + border-radius: .25rem +} + +.toast:not(:last-child) { + margin-bottom: .75rem +} + +.toast.showing { + opacity: 1 +} + +.toast.show { + display: block; + opacity: 1 +} + +.toast.hide { + display: none +} + +.toast-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: .25rem .75rem; + color: #6c757d; + background-color: rgba(255, 255, 255, .85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, .05) +} + +.toast-body { + padding: .75rem +} + +.modal-open { + overflow: hidden +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto +} + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0 +} + +.modal-dialog { + position: relative; + width: auto; + margin: .5rem; + pointer-events: none +} + +.modal.fade .modal-dialog { + transition: -webkit-transform .3s ease-out; + transition: transform .3s ease-out; + transition: transform .3s ease-out, -webkit-transform .3s ease-out; + -webkit-transform: translate(0, -50px); + transform: translate(0, -50px) +} + +@media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none + } +} + +.modal.show .modal-dialog { + -webkit-transform: none; + transform: none +} + +.modal.modal-static .modal-dialog { + -webkit-transform: scale(1.02); + transform: scale(1.02) +} + +.modal-dialog-scrollable { + display: -ms-flexbox; + display: flex; + max-height: calc(100% - 1rem) +} + +.modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden +} + +.modal-dialog-scrollable .modal-footer, .modal-dialog-scrollable .modal-header { + -ms-flex-negative: 0; + flex-shrink: 0 +} + +.modal-dialog-scrollable .modal-body { + overflow-y: auto +} + +.modal-dialog-centered { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + min-height: calc(100% - 1rem) +} + +.modal-dialog-centered::before { + display: block; + height: calc(100vh - 1rem); + content: "" +} + +.modal-dialog-centered.modal-dialog-scrollable { + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + height: 100% +} + +.modal-dialog-centered.modal-dialog-scrollable .modal-content { + max-height: none +} + +.modal-dialog-centered.modal-dialog-scrollable::before { + content: none +} + +.modal-content { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: .3rem; + outline: 0 +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000 +} + +.modal-backdrop.fade { + opacity: 0 +} + +.modal-backdrop.show { + opacity: .5 +} + +.modal-header { + display: -ms-flexbox; + display: flex; + -ms-flex-align: start; + align-items: flex-start; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: calc(.3rem - 1px); + border-top-right-radius: calc(.3rem - 1px) +} + +.modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5 +} + +.modal-body { + position: relative; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1rem +} + +.modal-footer { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: end; + justify-content: flex-end; + padding: .75rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: calc(.3rem - 1px); + border-bottom-left-radius: calc(.3rem - 1px) +} + +.modal-footer > * { + margin: .25rem +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto + } + + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem) + } + + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem) + } + + .modal-dialog-centered { + min-height: calc(100% - 3.5rem) + } + + .modal-dialog-centered::before { + height: calc(100vh - 3.5rem) + } + + .modal-sm { + max-width: 300px + } +} + +@media (min-width: 992px) { + .modal-lg, .modal-xl { + max-width: 800px + } +} + +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: .875rem; + word-wrap: break-word; + opacity: 0 +} + +.tooltip.show { + opacity: .9 +} + +.tooltip .arrow { + position: absolute; + display: block; + width: .8rem; + height: .4rem +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid +} + +.bs-tooltip-auto[x-placement^=top], .bs-tooltip-top { + padding: .4rem 0 +} + +.bs-tooltip-auto[x-placement^=top] .arrow, .bs-tooltip-top .arrow { + bottom: 0 +} + +.bs-tooltip-auto[x-placement^=top] .arrow::before, .bs-tooltip-top .arrow::before { + top: 0; + border-width: .4rem .4rem 0; + border-top-color: #000 +} + +.bs-tooltip-auto[x-placement^=right], .bs-tooltip-right { + padding: 0 .4rem +} + +.bs-tooltip-auto[x-placement^=right] .arrow, .bs-tooltip-right .arrow { + left: 0; + width: .4rem; + height: .8rem +} + +.bs-tooltip-auto[x-placement^=right] .arrow::before, .bs-tooltip-right .arrow::before { + right: 0; + border-width: .4rem .4rem .4rem 0; + border-right-color: #000 +} + +.bs-tooltip-auto[x-placement^=bottom], .bs-tooltip-bottom { + padding: .4rem 0 +} + +.bs-tooltip-auto[x-placement^=bottom] .arrow, .bs-tooltip-bottom .arrow { + top: 0 +} + +.bs-tooltip-auto[x-placement^=bottom] .arrow::before, .bs-tooltip-bottom .arrow::before { + bottom: 0; + border-width: 0 .4rem .4rem; + border-bottom-color: #000 +} + +.bs-tooltip-auto[x-placement^=left], .bs-tooltip-left { + padding: 0 .4rem +} + +.bs-tooltip-auto[x-placement^=left] .arrow, .bs-tooltip-left .arrow { + right: 0; + width: .4rem; + height: .8rem +} + +.bs-tooltip-auto[x-placement^=left] .arrow::before, .bs-tooltip-left .arrow::before { + left: 0; + border-width: .4rem 0 .4rem .4rem; + border-left-color: #000 +} + +.tooltip-inner { + max-width: 200px; + padding: .25rem .5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: .25rem +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: .875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: .3rem +} + +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: .5rem; + margin: 0 .3rem +} + +.popover .arrow::after, .popover .arrow::before { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid +} + +.bs-popover-auto[x-placement^=top], .bs-popover-top { + margin-bottom: .5rem +} + +.bs-popover-auto[x-placement^=top] > .arrow, .bs-popover-top > .arrow { + bottom: calc(-.5rem - 1px) +} + +.bs-popover-auto[x-placement^=top] > .arrow::before, .bs-popover-top > .arrow::before { + bottom: 0; + border-width: .5rem .5rem 0; + border-top-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=top] > .arrow::after, .bs-popover-top > .arrow::after { + bottom: 1px; + border-width: .5rem .5rem 0; + border-top-color: #fff +} + +.bs-popover-auto[x-placement^=right], .bs-popover-right { + margin-left: .5rem +} + +.bs-popover-auto[x-placement^=right] > .arrow, .bs-popover-right > .arrow { + left: calc(-.5rem - 1px); + width: .5rem; + height: 1rem; + margin: .3rem 0 +} + +.bs-popover-auto[x-placement^=right] > .arrow::before, .bs-popover-right > .arrow::before { + left: 0; + border-width: .5rem .5rem .5rem 0; + border-right-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=right] > .arrow::after, .bs-popover-right > .arrow::after { + left: 1px; + border-width: .5rem .5rem .5rem 0; + border-right-color: #fff +} + +.bs-popover-auto[x-placement^=bottom], .bs-popover-bottom { + margin-top: .5rem +} + +.bs-popover-auto[x-placement^=bottom] > .arrow, .bs-popover-bottom > .arrow { + top: calc(-.5rem - 1px) +} + +.bs-popover-auto[x-placement^=bottom] > .arrow::before, .bs-popover-bottom > .arrow::before { + top: 0; + border-width: 0 .5rem .5rem .5rem; + border-bottom-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=bottom] > .arrow::after, .bs-popover-bottom > .arrow::after { + top: 1px; + border-width: 0 .5rem .5rem .5rem; + border-bottom-color: #fff +} + +.bs-popover-auto[x-placement^=bottom] .popover-header::before, .bs-popover-bottom .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7 +} + +.bs-popover-auto[x-placement^=left], .bs-popover-left { + margin-right: .5rem +} + +.bs-popover-auto[x-placement^=left] > .arrow, .bs-popover-left > .arrow { + right: calc(-.5rem - 1px); + width: .5rem; + height: 1rem; + margin: .3rem 0 +} + +.bs-popover-auto[x-placement^=left] > .arrow::before, .bs-popover-left > .arrow::before { + right: 0; + border-width: .5rem 0 .5rem .5rem; + border-left-color: rgba(0, 0, 0, .25) +} + +.bs-popover-auto[x-placement^=left] > .arrow::after, .bs-popover-left > .arrow::after { + right: 1px; + border-width: .5rem 0 .5rem .5rem; + border-left-color: #fff +} + +.popover-header { + padding: .5rem .75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(.3rem - 1px); + border-top-right-radius: calc(.3rem - 1px) +} + +.popover-header:empty { + display: none +} + +.popover-body { + padding: .5rem .75rem; + color: #212529 +} + +.carousel { + position: relative +} + +.carousel.pointer-event { + -ms-touch-action: pan-y; + touch-action: pan-y +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden +} + +.carousel-inner::after { + display: block; + clear: both; + content: "" +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transition: -webkit-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + transition: transform .6s ease-in-out, -webkit-transform .6s ease-in-out +} + +@media (prefers-reduced-motion: reduce) { + .carousel-item { + transition: none + } +} + +.carousel-item-next, .carousel-item-prev, .carousel-item.active { + display: block +} + +.active.carousel-item-right, .carousel-item-next:not(.carousel-item-left) { + -webkit-transform: translateX(100%); + transform: translateX(100%) +} + +.active.carousel-item-left, .carousel-item-prev:not(.carousel-item-right) { + -webkit-transform: translateX(-100%); + transform: translateX(-100%) +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + -webkit-transform: none; + transform: none +} + +.carousel-fade .carousel-item-next.carousel-item-left, .carousel-fade .carousel-item-prev.carousel-item-right, .carousel-fade .carousel-item.active { + z-index: 1; + opacity: 1 +} + +.carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: opacity 0s .6s +} + +@media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, .carousel-fade .active.carousel-item-right { + transition: none + } +} + +.carousel-control-next, .carousel-control-prev { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: .5; + transition: opacity .15s ease +} + +@media (prefers-reduced-motion: reduce) { + .carousel-control-next, .carousel-control-prev { + transition: none + } +} + +.carousel-control-next:focus, .carousel-control-next:hover, .carousel-control-prev:focus, .carousel-control-prev:hover { + color: #fff; + text-decoration: none; + outline: 0; + opacity: .9 +} + +.carousel-control-prev { + left: 0 +} + +.carousel-control-next { + right: 0 +} + +.carousel-control-next-icon, .carousel-control-prev-icon { + display: inline-block; + width: 20px; + height: 20px; + background: no-repeat 50%/100% 100% +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e") +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e") +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none +} + +.carousel-indicators li { + box-sizing: content-box; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity .6s ease +} + +@media (prefers-reduced-motion: reduce) { + .carousel-indicators li { + transition: none + } +} + +.carousel-indicators .active { + opacity: 1 +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center +} + +@-webkit-keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg) + } +} + +@keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg) + } +} + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: .25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + -webkit-animation: spinner-border .75s linear infinite; + animation: spinner-border .75s linear infinite +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: .2em +} + +@-webkit-keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0) + } + 50% { + opacity: 1 + } +} + +@keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0) + } + 50% { + opacity: 1 + } +} + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + -webkit-animation: spinner-grow .75s linear infinite; + animation: spinner-grow .75s linear infinite +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem +} + +.align-baseline { + vertical-align: baseline !important +} + +.align-top { + vertical-align: top !important +} + +.align-middle { + vertical-align: middle !important +} + +.align-bottom { + vertical-align: bottom !important +} + +.align-text-bottom { + vertical-align: text-bottom !important +} + +.align-text-top { + vertical-align: text-top !important +} + +.bg-primary { + background-color: #007bff !important +} + +a.bg-primary:focus, a.bg-primary:hover, button.bg-primary:focus, button.bg-primary:hover { + background-color: #0062cc !important +} + +.bg-secondary { + background-color: #6c757d !important +} + +a.bg-secondary:focus, a.bg-secondary:hover, button.bg-secondary:focus, button.bg-secondary:hover { + background-color: #545b62 !important +} + +.bg-success { + background-color: #28a745 !important +} + +a.bg-success:focus, a.bg-success:hover, button.bg-success:focus, button.bg-success:hover { + background-color: #1e7e34 !important +} + +.bg-info { + background-color: #17a2b8 !important +} + +a.bg-info:focus, a.bg-info:hover, button.bg-info:focus, button.bg-info:hover { + background-color: #117a8b !important +} + +.bg-warning { + background-color: #ffc107 !important +} + +a.bg-warning:focus, a.bg-warning:hover, button.bg-warning:focus, button.bg-warning:hover { + background-color: #d39e00 !important +} + +.bg-danger { + background-color: #dc3545 !important +} + +a.bg-danger:focus, a.bg-danger:hover, button.bg-danger:focus, button.bg-danger:hover { + background-color: #bd2130 !important +} + +.bg-light { + background-color: #f8f9fa !important +} + +a.bg-light:focus, a.bg-light:hover, button.bg-light:focus, button.bg-light:hover { + background-color: #dae0e5 !important +} + +.bg-dark { + background-color: #343a40 !important +} + +a.bg-dark:focus, a.bg-dark:hover, button.bg-dark:focus, button.bg-dark:hover { + background-color: #1d2124 !important +} + +.bg-white { + background-color: #fff !important +} + +.bg-transparent { + background-color: transparent !important +} + +.border { + border: 1px solid #dee2e6 !important +} + +.border-top { + border-top: 1px solid #dee2e6 !important +} + +.border-right { + border-right: 1px solid #dee2e6 !important +} + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important +} + +.border-left { + border-left: 1px solid #dee2e6 !important +} + +.border-0 { + border: 0 !important +} + +.border-top-0 { + border-top: 0 !important +} + +.border-right-0 { + border-right: 0 !important +} + +.border-bottom-0 { + border-bottom: 0 !important +} + +.border-left-0 { + border-left: 0 !important +} + +.border-primary { + border-color: #007bff !important +} + +.border-secondary { + border-color: #6c757d !important +} + +.border-success { + border-color: #28a745 !important +} + +.border-info { + border-color: #17a2b8 !important +} + +.border-warning { + border-color: #ffc107 !important +} + +.border-danger { + border-color: #dc3545 !important +} + +.border-light { + border-color: #f8f9fa !important +} + +.border-dark { + border-color: #343a40 !important +} + +.border-white { + border-color: #fff !important +} + +.rounded-sm { + border-radius: .2rem !important +} + +.rounded { + border-radius: .25rem !important +} + +.rounded-top { + border-top-left-radius: .25rem !important; + border-top-right-radius: .25rem !important +} + +.rounded-right { + border-top-right-radius: .25rem !important; + border-bottom-right-radius: .25rem !important +} + +.rounded-bottom { + border-bottom-right-radius: .25rem !important; + border-bottom-left-radius: .25rem !important +} + +.rounded-left { + border-top-left-radius: .25rem !important; + border-bottom-left-radius: .25rem !important +} + +.rounded-lg { + border-radius: .3rem !important +} + +.rounded-circle { + border-radius: 50% !important +} + +.rounded-pill { + border-radius: 50rem !important +} + +.rounded-0 { + border-radius: 0 !important +} + +.clearfix::after { + display: block; + clear: both; + content: "" +} + +.d-none { + display: none !important +} + +.d-inline { + display: inline !important +} + +.d-inline-block { + display: inline-block !important +} + +.d-block { + display: block !important +} + +.d-table { + display: table !important +} + +.d-table-row { + display: table-row !important +} + +.d-table-cell { + display: table-cell !important +} + +.d-flex { + display: -ms-flexbox !important; + display: flex !important +} + +.d-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important + } + + .d-sm-inline { + display: inline !important + } + + .d-sm-inline-block { + display: inline-block !important + } + + .d-sm-block { + display: block !important + } + + .d-sm-table { + display: table !important + } + + .d-sm-table-row { + display: table-row !important + } + + .d-sm-table-cell { + display: table-cell !important + } + + .d-sm-flex { + display: -ms-flexbox !important; + display: flex !important + } + + .d-sm-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important + } + + .d-md-inline { + display: inline !important + } + + .d-md-inline-block { + display: inline-block !important + } + + .d-md-block { + display: block !important + } + + .d-md-table { + display: table !important + } + + .d-md-table-row { + display: table-row !important + } + + .d-md-table-cell { + display: table-cell !important + } + + .d-md-flex { + display: -ms-flexbox !important; + display: flex !important + } + + .d-md-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important + } + + .d-lg-inline { + display: inline !important + } + + .d-lg-inline-block { + display: inline-block !important + } + + .d-lg-block { + display: block !important + } + + .d-lg-table { + display: table !important + } + + .d-lg-table-row { + display: table-row !important + } + + .d-lg-table-cell { + display: table-cell !important + } + + .d-lg-flex { + display: -ms-flexbox !important; + display: flex !important + } + + .d-lg-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important + } + + .d-xl-inline { + display: inline !important + } + + .d-xl-inline-block { + display: inline-block !important + } + + .d-xl-block { + display: block !important + } + + .d-xl-table { + display: table !important + } + + .d-xl-table-row { + display: table-row !important + } + + .d-xl-table-cell { + display: table-cell !important + } + + .d-xl-flex { + display: -ms-flexbox !important; + display: flex !important + } + + .d-xl-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important + } +} + +@media print { + .d-print-none { + display: none !important + } + + .d-print-inline { + display: inline !important + } + + .d-print-inline-block { + display: inline-block !important + } + + .d-print-block { + display: block !important + } + + .d-print-table { + display: table !important + } + + .d-print-table-row { + display: table-row !important + } + + .d-print-table-cell { + display: table-cell !important + } + + .d-print-flex { + display: -ms-flexbox !important; + display: flex !important + } + + .d-print-inline-flex { + display: -ms-inline-flexbox !important; + display: inline-flex !important + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden +} + +.embed-responsive::before { + display: block; + content: "" +} + +.embed-responsive .embed-responsive-item, .embed-responsive embed, .embed-responsive iframe, .embed-responsive object, .embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0 +} + +.embed-responsive-21by9::before { + padding-top: 42.857143% +} + +.embed-responsive-16by9::before { + padding-top: 56.25% +} + +.embed-responsive-4by3::before { + padding-top: 75% +} + +.embed-responsive-1by1::before { + padding-top: 100% +} + +.flex-row { + -ms-flex-direction: row !important; + flex-direction: row !important +} + +.flex-column { + -ms-flex-direction: column !important; + flex-direction: column !important +} + +.flex-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important +} + +.flex-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important +} + +.flex-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important +} + +.flex-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important +} + +.flex-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important +} + +.flex-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important +} + +.flex-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important +} + +.flex-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important +} + +.flex-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important +} + +.flex-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important +} + +.justify-content-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important +} + +.justify-content-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important +} + +.justify-content-center { + -ms-flex-pack: center !important; + justify-content: center !important +} + +.justify-content-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important +} + +.justify-content-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important +} + +.align-items-start { + -ms-flex-align: start !important; + align-items: flex-start !important +} + +.align-items-end { + -ms-flex-align: end !important; + align-items: flex-end !important +} + +.align-items-center { + -ms-flex-align: center !important; + align-items: center !important +} + +.align-items-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important +} + +.align-items-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important +} + +.align-content-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important +} + +.align-content-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important +} + +.align-content-center { + -ms-flex-line-pack: center !important; + align-content: center !important +} + +.align-content-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important +} + +.align-content-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important +} + +.align-content-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important +} + +.align-self-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important +} + +.align-self-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important +} + +.align-self-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important +} + +.align-self-center { + -ms-flex-item-align: center !important; + align-self: center !important +} + +.align-self-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important +} + +.align-self-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important +} + +@media (min-width: 576px) { + .flex-sm-row { + -ms-flex-direction: row !important; + flex-direction: row !important + } + + .flex-sm-column { + -ms-flex-direction: column !important; + flex-direction: column !important + } + + .flex-sm-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important + } + + .flex-sm-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important + } + + .flex-sm-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important + } + + .flex-sm-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important + } + + .flex-sm-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important + } + + .flex-sm-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important + } + + .flex-sm-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important + } + + .flex-sm-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important + } + + .flex-sm-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important + } + + .flex-sm-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important + } + + .justify-content-sm-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important + } + + .justify-content-sm-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important + } + + .justify-content-sm-center { + -ms-flex-pack: center !important; + justify-content: center !important + } + + .justify-content-sm-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important + } + + .justify-content-sm-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important + } + + .align-items-sm-start { + -ms-flex-align: start !important; + align-items: flex-start !important + } + + .align-items-sm-end { + -ms-flex-align: end !important; + align-items: flex-end !important + } + + .align-items-sm-center { + -ms-flex-align: center !important; + align-items: center !important + } + + .align-items-sm-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important + } + + .align-items-sm-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important + } + + .align-content-sm-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important + } + + .align-content-sm-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important + } + + .align-content-sm-center { + -ms-flex-line-pack: center !important; + align-content: center !important + } + + .align-content-sm-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important + } + + .align-content-sm-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important + } + + .align-content-sm-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important + } + + .align-self-sm-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important + } + + .align-self-sm-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important + } + + .align-self-sm-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important + } + + .align-self-sm-center { + -ms-flex-item-align: center !important; + align-self: center !important + } + + .align-self-sm-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important + } + + .align-self-sm-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important + } +} + +@media (min-width: 768px) { + .flex-md-row { + -ms-flex-direction: row !important; + flex-direction: row !important + } + + .flex-md-column { + -ms-flex-direction: column !important; + flex-direction: column !important + } + + .flex-md-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important + } + + .flex-md-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important + } + + .flex-md-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important + } + + .flex-md-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important + } + + .flex-md-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important + } + + .flex-md-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important + } + + .flex-md-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important + } + + .flex-md-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important + } + + .flex-md-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important + } + + .flex-md-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important + } + + .justify-content-md-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important + } + + .justify-content-md-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important + } + + .justify-content-md-center { + -ms-flex-pack: center !important; + justify-content: center !important + } + + .justify-content-md-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important + } + + .justify-content-md-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important + } + + .align-items-md-start { + -ms-flex-align: start !important; + align-items: flex-start !important + } + + .align-items-md-end { + -ms-flex-align: end !important; + align-items: flex-end !important + } + + .align-items-md-center { + -ms-flex-align: center !important; + align-items: center !important + } + + .align-items-md-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important + } + + .align-items-md-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important + } + + .align-content-md-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important + } + + .align-content-md-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important + } + + .align-content-md-center { + -ms-flex-line-pack: center !important; + align-content: center !important + } + + .align-content-md-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important + } + + .align-content-md-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important + } + + .align-content-md-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important + } + + .align-self-md-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important + } + + .align-self-md-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important + } + + .align-self-md-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important + } + + .align-self-md-center { + -ms-flex-item-align: center !important; + align-self: center !important + } + + .align-self-md-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important + } + + .align-self-md-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important + } +} + +@media (min-width: 992px) { + .flex-lg-row { + -ms-flex-direction: row !important; + flex-direction: row !important + } + + .flex-lg-column { + -ms-flex-direction: column !important; + flex-direction: column !important + } + + .flex-lg-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important + } + + .flex-lg-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important + } + + .flex-lg-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important + } + + .flex-lg-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important + } + + .flex-lg-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important + } + + .flex-lg-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important + } + + .flex-lg-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important + } + + .flex-lg-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important + } + + .flex-lg-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important + } + + .flex-lg-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important + } + + .justify-content-lg-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important + } + + .justify-content-lg-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important + } + + .justify-content-lg-center { + -ms-flex-pack: center !important; + justify-content: center !important + } + + .justify-content-lg-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important + } + + .justify-content-lg-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important + } + + .align-items-lg-start { + -ms-flex-align: start !important; + align-items: flex-start !important + } + + .align-items-lg-end { + -ms-flex-align: end !important; + align-items: flex-end !important + } + + .align-items-lg-center { + -ms-flex-align: center !important; + align-items: center !important + } + + .align-items-lg-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important + } + + .align-items-lg-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important + } + + .align-content-lg-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important + } + + .align-content-lg-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important + } + + .align-content-lg-center { + -ms-flex-line-pack: center !important; + align-content: center !important + } + + .align-content-lg-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important + } + + .align-content-lg-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important + } + + .align-content-lg-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important + } + + .align-self-lg-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important + } + + .align-self-lg-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important + } + + .align-self-lg-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important + } + + .align-self-lg-center { + -ms-flex-item-align: center !important; + align-self: center !important + } + + .align-self-lg-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important + } + + .align-self-lg-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + -ms-flex-direction: row !important; + flex-direction: row !important + } + + .flex-xl-column { + -ms-flex-direction: column !important; + flex-direction: column !important + } + + .flex-xl-row-reverse { + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important + } + + .flex-xl-column-reverse { + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important + } + + .flex-xl-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important + } + + .flex-xl-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important + } + + .flex-xl-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important + } + + .flex-xl-fill { + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important + } + + .flex-xl-grow-0 { + -ms-flex-positive: 0 !important; + flex-grow: 0 !important + } + + .flex-xl-grow-1 { + -ms-flex-positive: 1 !important; + flex-grow: 1 !important + } + + .flex-xl-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important + } + + .flex-xl-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important + } + + .justify-content-xl-start { + -ms-flex-pack: start !important; + justify-content: flex-start !important + } + + .justify-content-xl-end { + -ms-flex-pack: end !important; + justify-content: flex-end !important + } + + .justify-content-xl-center { + -ms-flex-pack: center !important; + justify-content: center !important + } + + .justify-content-xl-between { + -ms-flex-pack: justify !important; + justify-content: space-between !important + } + + .justify-content-xl-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important + } + + .align-items-xl-start { + -ms-flex-align: start !important; + align-items: flex-start !important + } + + .align-items-xl-end { + -ms-flex-align: end !important; + align-items: flex-end !important + } + + .align-items-xl-center { + -ms-flex-align: center !important; + align-items: center !important + } + + .align-items-xl-baseline { + -ms-flex-align: baseline !important; + align-items: baseline !important + } + + .align-items-xl-stretch { + -ms-flex-align: stretch !important; + align-items: stretch !important + } + + .align-content-xl-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important + } + + .align-content-xl-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important + } + + .align-content-xl-center { + -ms-flex-line-pack: center !important; + align-content: center !important + } + + .align-content-xl-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important + } + + .align-content-xl-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important + } + + .align-content-xl-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important + } + + .align-self-xl-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important + } + + .align-self-xl-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important + } + + .align-self-xl-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important + } + + .align-self-xl-center { + -ms-flex-item-align: center !important; + align-self: center !important + } + + .align-self-xl-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important + } + + .align-self-xl-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important + } +} + +.float-left { + float: left !important +} + +.float-right { + float: right !important +} + +.float-none { + float: none !important +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important + } + + .float-sm-right { + float: right !important + } + + .float-sm-none { + float: none !important + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important + } + + .float-md-right { + float: right !important + } + + .float-md-none { + float: none !important + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important + } + + .float-lg-right { + float: right !important + } + + .float-lg-none { + float: none !important + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important + } + + .float-xl-right { + float: right !important + } + + .float-xl-none { + float: none !important + } +} + +.overflow-auto { + overflow: auto !important +} + +.overflow-hidden { + overflow: hidden !important +} + +.position-static { + position: static !important +} + +.position-relative { + position: relative !important +} + +.position-absolute { + position: absolute !important +} + +.position-fixed { + position: fixed !important +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030 +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030 +} + +@supports ((position:-webkit-sticky) or (position:sticky)) { + .sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020 + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0 +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal +} + +.shadow-sm { + box-shadow: 0 .125rem .25rem rgba(0, 0, 0, .075) !important +} + +.shadow { + box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !important +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, .175) !important +} + +.shadow-none { + box-shadow: none !important +} + +.w-25 { + width: 25% !important +} + +.w-50 { + width: 50% !important +} + +.w-75 { + width: 75% !important +} + +.w-100 { + width: 100% !important +} + +.w-auto { + width: auto !important +} + +.h-25 { + height: 25% !important +} + +.h-50 { + height: 50% !important +} + +.h-75 { + height: 75% !important +} + +.h-100 { + height: 100% !important +} + +.h-auto { + height: auto !important +} + +.mw-100 { + max-width: 100% !important +} + +.mh-100 { + max-height: 100% !important +} + +.min-vw-100 { + min-width: 100vw !important +} + +.min-vh-100 { + min-height: 100vh !important +} + +.vw-100 { + width: 100vw !important +} + +.vh-100 { + height: 100vh !important +} + +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0) +} + +.m-0 { + margin: 0 !important +} + +.mt-0, .my-0 { + margin-top: 0 !important +} + +.mr-0, .mx-0 { + margin-right: 0 !important +} + +.mb-0, .my-0 { + margin-bottom: 0 !important +} + +.ml-0, .mx-0 { + margin-left: 0 !important +} + +.m-1 { + margin: .25rem !important +} + +.mt-1, .my-1 { + margin-top: .25rem !important +} + +.mr-1, .mx-1 { + margin-right: .25rem !important +} + +.mb-1, .my-1 { + margin-bottom: .25rem !important +} + +.ml-1, .mx-1 { + margin-left: .25rem !important +} + +.m-2 { + margin: .5rem !important +} + +.mt-2, .my-2 { + margin-top: .5rem !important +} + +.mr-2, .mx-2 { + margin-right: .5rem !important +} + +.mb-2, .my-2 { + margin-bottom: .5rem !important +} + +.ml-2, .mx-2 { + margin-left: .5rem !important +} + +.m-3 { + margin: 1rem !important +} + +.mt-3, .my-3 { + margin-top: 1rem !important +} + +.mr-3, .mx-3 { + margin-right: 1rem !important +} + +.mb-3, .my-3 { + margin-bottom: 1rem !important +} + +.ml-3, .mx-3 { + margin-left: 1rem !important +} + +.m-4 { + margin: 1.5rem !important +} + +.mt-4, .my-4 { + margin-top: 1.5rem !important +} + +.mr-4, .mx-4 { + margin-right: 1.5rem !important +} + +.mb-4, .my-4 { + margin-bottom: 1.5rem !important +} + +.ml-4, .mx-4 { + margin-left: 1.5rem !important +} + +.m-5 { + margin: 3rem !important +} + +.mt-5, .my-5 { + margin-top: 3rem !important +} + +.mr-5, .mx-5 { + margin-right: 3rem !important +} + +.mb-5, .my-5 { + margin-bottom: 3rem !important +} + +.ml-5, .mx-5 { + margin-left: 3rem !important +} + +.p-0 { + padding: 0 !important +} + +.pt-0, .py-0 { + padding-top: 0 !important +} + +.pr-0, .px-0 { + padding-right: 0 !important +} + +.pb-0, .py-0 { + padding-bottom: 0 !important +} + +.pl-0, .px-0 { + padding-left: 0 !important +} + +.p-1 { + padding: .25rem !important +} + +.pt-1, .py-1 { + padding-top: .25rem !important +} + +.pr-1, .px-1 { + padding-right: .25rem !important +} + +.pb-1, .py-1 { + padding-bottom: .25rem !important +} + +.pl-1, .px-1 { + padding-left: .25rem !important +} + +.p-2 { + padding: .5rem !important +} + +.pt-2, .py-2 { + padding-top: .5rem !important +} + +.pr-2, .px-2 { + padding-right: .5rem !important +} + +.pb-2, .py-2 { + padding-bottom: .5rem !important +} + +.pl-2, .px-2 { + padding-left: .5rem !important +} + +.p-3 { + padding: 1rem !important +} + +.pt-3, .py-3 { + padding-top: 1rem !important +} + +.pr-3, .px-3 { + padding-right: 1rem !important +} + +.pb-3, .py-3 { + padding-bottom: 1rem !important +} + +.pl-3, .px-3 { + padding-left: 1rem !important +} + +.p-4 { + padding: 1.5rem !important +} + +.pt-4, .py-4 { + padding-top: 1.5rem !important +} + +.pr-4, .px-4 { + padding-right: 1.5rem !important +} + +.pb-4, .py-4 { + padding-bottom: 1.5rem !important +} + +.pl-4, .px-4 { + padding-left: 1.5rem !important +} + +.p-5 { + padding: 3rem !important +} + +.pt-5, .py-5 { + padding-top: 3rem !important +} + +.pr-5, .px-5 { + padding-right: 3rem !important +} + +.pb-5, .py-5 { + padding-bottom: 3rem !important +} + +.pl-5, .px-5 { + padding-left: 3rem !important +} + +.m-n1 { + margin: -.25rem !important +} + +.mt-n1, .my-n1 { + margin-top: -.25rem !important +} + +.mr-n1, .mx-n1 { + margin-right: -.25rem !important +} + +.mb-n1, .my-n1 { + margin-bottom: -.25rem !important +} + +.ml-n1, .mx-n1 { + margin-left: -.25rem !important +} + +.m-n2 { + margin: -.5rem !important +} + +.mt-n2, .my-n2 { + margin-top: -.5rem !important +} + +.mr-n2, .mx-n2 { + margin-right: -.5rem !important +} + +.mb-n2, .my-n2 { + margin-bottom: -.5rem !important +} + +.ml-n2, .mx-n2 { + margin-left: -.5rem !important +} + +.m-n3 { + margin: -1rem !important +} + +.mt-n3, .my-n3 { + margin-top: -1rem !important +} + +.mr-n3, .mx-n3 { + margin-right: -1rem !important +} + +.mb-n3, .my-n3 { + margin-bottom: -1rem !important +} + +.ml-n3, .mx-n3 { + margin-left: -1rem !important +} + +.m-n4 { + margin: -1.5rem !important +} + +.mt-n4, .my-n4 { + margin-top: -1.5rem !important +} + +.mr-n4, .mx-n4 { + margin-right: -1.5rem !important +} + +.mb-n4, .my-n4 { + margin-bottom: -1.5rem !important +} + +.ml-n4, .mx-n4 { + margin-left: -1.5rem !important +} + +.m-n5 { + margin: -3rem !important +} + +.mt-n5, .my-n5 { + margin-top: -3rem !important +} + +.mr-n5, .mx-n5 { + margin-right: -3rem !important +} + +.mb-n5, .my-n5 { + margin-bottom: -3rem !important +} + +.ml-n5, .mx-n5 { + margin-left: -3rem !important +} + +.m-auto { + margin: auto !important +} + +.mt-auto, .my-auto { + margin-top: auto !important +} + +.mr-auto, .mx-auto { + margin-right: auto !important +} + +.mb-auto, .my-auto { + margin-bottom: auto !important +} + +.ml-auto, .mx-auto { + margin-left: auto !important +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important + } + + .mt-sm-0, .my-sm-0 { + margin-top: 0 !important + } + + .mr-sm-0, .mx-sm-0 { + margin-right: 0 !important + } + + .mb-sm-0, .my-sm-0 { + margin-bottom: 0 !important + } + + .ml-sm-0, .mx-sm-0 { + margin-left: 0 !important + } + + .m-sm-1 { + margin: .25rem !important + } + + .mt-sm-1, .my-sm-1 { + margin-top: .25rem !important + } + + .mr-sm-1, .mx-sm-1 { + margin-right: .25rem !important + } + + .mb-sm-1, .my-sm-1 { + margin-bottom: .25rem !important + } + + .ml-sm-1, .mx-sm-1 { + margin-left: .25rem !important + } + + .m-sm-2 { + margin: .5rem !important + } + + .mt-sm-2, .my-sm-2 { + margin-top: .5rem !important + } + + .mr-sm-2, .mx-sm-2 { + margin-right: .5rem !important + } + + .mb-sm-2, .my-sm-2 { + margin-bottom: .5rem !important + } + + .ml-sm-2, .mx-sm-2 { + margin-left: .5rem !important + } + + .m-sm-3 { + margin: 1rem !important + } + + .mt-sm-3, .my-sm-3 { + margin-top: 1rem !important + } + + .mr-sm-3, .mx-sm-3 { + margin-right: 1rem !important + } + + .mb-sm-3, .my-sm-3 { + margin-bottom: 1rem !important + } + + .ml-sm-3, .mx-sm-3 { + margin-left: 1rem !important + } + + .m-sm-4 { + margin: 1.5rem !important + } + + .mt-sm-4, .my-sm-4 { + margin-top: 1.5rem !important + } + + .mr-sm-4, .mx-sm-4 { + margin-right: 1.5rem !important + } + + .mb-sm-4, .my-sm-4 { + margin-bottom: 1.5rem !important + } + + .ml-sm-4, .mx-sm-4 { + margin-left: 1.5rem !important + } + + .m-sm-5 { + margin: 3rem !important + } + + .mt-sm-5, .my-sm-5 { + margin-top: 3rem !important + } + + .mr-sm-5, .mx-sm-5 { + margin-right: 3rem !important + } + + .mb-sm-5, .my-sm-5 { + margin-bottom: 3rem !important + } + + .ml-sm-5, .mx-sm-5 { + margin-left: 3rem !important + } + + .p-sm-0 { + padding: 0 !important + } + + .pt-sm-0, .py-sm-0 { + padding-top: 0 !important + } + + .pr-sm-0, .px-sm-0 { + padding-right: 0 !important + } + + .pb-sm-0, .py-sm-0 { + padding-bottom: 0 !important + } + + .pl-sm-0, .px-sm-0 { + padding-left: 0 !important + } + + .p-sm-1 { + padding: .25rem !important + } + + .pt-sm-1, .py-sm-1 { + padding-top: .25rem !important + } + + .pr-sm-1, .px-sm-1 { + padding-right: .25rem !important + } + + .pb-sm-1, .py-sm-1 { + padding-bottom: .25rem !important + } + + .pl-sm-1, .px-sm-1 { + padding-left: .25rem !important + } + + .p-sm-2 { + padding: .5rem !important + } + + .pt-sm-2, .py-sm-2 { + padding-top: .5rem !important + } + + .pr-sm-2, .px-sm-2 { + padding-right: .5rem !important + } + + .pb-sm-2, .py-sm-2 { + padding-bottom: .5rem !important + } + + .pl-sm-2, .px-sm-2 { + padding-left: .5rem !important + } + + .p-sm-3 { + padding: 1rem !important + } + + .pt-sm-3, .py-sm-3 { + padding-top: 1rem !important + } + + .pr-sm-3, .px-sm-3 { + padding-right: 1rem !important + } + + .pb-sm-3, .py-sm-3 { + padding-bottom: 1rem !important + } + + .pl-sm-3, .px-sm-3 { + padding-left: 1rem !important + } + + .p-sm-4 { + padding: 1.5rem !important + } + + .pt-sm-4, .py-sm-4 { + padding-top: 1.5rem !important + } + + .pr-sm-4, .px-sm-4 { + padding-right: 1.5rem !important + } + + .pb-sm-4, .py-sm-4 { + padding-bottom: 1.5rem !important + } + + .pl-sm-4, .px-sm-4 { + padding-left: 1.5rem !important + } + + .p-sm-5 { + padding: 3rem !important + } + + .pt-sm-5, .py-sm-5 { + padding-top: 3rem !important + } + + .pr-sm-5, .px-sm-5 { + padding-right: 3rem !important + } + + .pb-sm-5, .py-sm-5 { + padding-bottom: 3rem !important + } + + .pl-sm-5, .px-sm-5 { + padding-left: 3rem !important + } + + .m-sm-n1 { + margin: -.25rem !important + } + + .mt-sm-n1, .my-sm-n1 { + margin-top: -.25rem !important + } + + .mr-sm-n1, .mx-sm-n1 { + margin-right: -.25rem !important + } + + .mb-sm-n1, .my-sm-n1 { + margin-bottom: -.25rem !important + } + + .ml-sm-n1, .mx-sm-n1 { + margin-left: -.25rem !important + } + + .m-sm-n2 { + margin: -.5rem !important + } + + .mt-sm-n2, .my-sm-n2 { + margin-top: -.5rem !important + } + + .mr-sm-n2, .mx-sm-n2 { + margin-right: -.5rem !important + } + + .mb-sm-n2, .my-sm-n2 { + margin-bottom: -.5rem !important + } + + .ml-sm-n2, .mx-sm-n2 { + margin-left: -.5rem !important + } + + .m-sm-n3 { + margin: -1rem !important + } + + .mt-sm-n3, .my-sm-n3 { + margin-top: -1rem !important + } + + .mr-sm-n3, .mx-sm-n3 { + margin-right: -1rem !important + } + + .mb-sm-n3, .my-sm-n3 { + margin-bottom: -1rem !important + } + + .ml-sm-n3, .mx-sm-n3 { + margin-left: -1rem !important + } + + .m-sm-n4 { + margin: -1.5rem !important + } + + .mt-sm-n4, .my-sm-n4 { + margin-top: -1.5rem !important + } + + .mr-sm-n4, .mx-sm-n4 { + margin-right: -1.5rem !important + } + + .mb-sm-n4, .my-sm-n4 { + margin-bottom: -1.5rem !important + } + + .ml-sm-n4, .mx-sm-n4 { + margin-left: -1.5rem !important + } + + .m-sm-n5 { + margin: -3rem !important + } + + .mt-sm-n5, .my-sm-n5 { + margin-top: -3rem !important + } + + .mr-sm-n5, .mx-sm-n5 { + margin-right: -3rem !important + } + + .mb-sm-n5, .my-sm-n5 { + margin-bottom: -3rem !important + } + + .ml-sm-n5, .mx-sm-n5 { + margin-left: -3rem !important + } + + .m-sm-auto { + margin: auto !important + } + + .mt-sm-auto, .my-sm-auto { + margin-top: auto !important + } + + .mr-sm-auto, .mx-sm-auto { + margin-right: auto !important + } + + .mb-sm-auto, .my-sm-auto { + margin-bottom: auto !important + } + + .ml-sm-auto, .mx-sm-auto { + margin-left: auto !important + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important + } + + .mt-md-0, .my-md-0 { + margin-top: 0 !important + } + + .mr-md-0, .mx-md-0 { + margin-right: 0 !important + } + + .mb-md-0, .my-md-0 { + margin-bottom: 0 !important + } + + .ml-md-0, .mx-md-0 { + margin-left: 0 !important + } + + .m-md-1 { + margin: .25rem !important + } + + .mt-md-1, .my-md-1 { + margin-top: .25rem !important + } + + .mr-md-1, .mx-md-1 { + margin-right: .25rem !important + } + + .mb-md-1, .my-md-1 { + margin-bottom: .25rem !important + } + + .ml-md-1, .mx-md-1 { + margin-left: .25rem !important + } + + .m-md-2 { + margin: .5rem !important + } + + .mt-md-2, .my-md-2 { + margin-top: .5rem !important + } + + .mr-md-2, .mx-md-2 { + margin-right: .5rem !important + } + + .mb-md-2, .my-md-2 { + margin-bottom: .5rem !important + } + + .ml-md-2, .mx-md-2 { + margin-left: .5rem !important + } + + .m-md-3 { + margin: 1rem !important + } + + .mt-md-3, .my-md-3 { + margin-top: 1rem !important + } + + .mr-md-3, .mx-md-3 { + margin-right: 1rem !important + } + + .mb-md-3, .my-md-3 { + margin-bottom: 1rem !important + } + + .ml-md-3, .mx-md-3 { + margin-left: 1rem !important + } + + .m-md-4 { + margin: 1.5rem !important + } + + .mt-md-4, .my-md-4 { + margin-top: 1.5rem !important + } + + .mr-md-4, .mx-md-4 { + margin-right: 1.5rem !important + } + + .mb-md-4, .my-md-4 { + margin-bottom: 1.5rem !important + } + + .ml-md-4, .mx-md-4 { + margin-left: 1.5rem !important + } + + .m-md-5 { + margin: 3rem !important + } + + .mt-md-5, .my-md-5 { + margin-top: 3rem !important + } + + .mr-md-5, .mx-md-5 { + margin-right: 3rem !important + } + + .mb-md-5, .my-md-5 { + margin-bottom: 3rem !important + } + + .ml-md-5, .mx-md-5 { + margin-left: 3rem !important + } + + .p-md-0 { + padding: 0 !important + } + + .pt-md-0, .py-md-0 { + padding-top: 0 !important + } + + .pr-md-0, .px-md-0 { + padding-right: 0 !important + } + + .pb-md-0, .py-md-0 { + padding-bottom: 0 !important + } + + .pl-md-0, .px-md-0 { + padding-left: 0 !important + } + + .p-md-1 { + padding: .25rem !important + } + + .pt-md-1, .py-md-1 { + padding-top: .25rem !important + } + + .pr-md-1, .px-md-1 { + padding-right: .25rem !important + } + + .pb-md-1, .py-md-1 { + padding-bottom: .25rem !important + } + + .pl-md-1, .px-md-1 { + padding-left: .25rem !important + } + + .p-md-2 { + padding: .5rem !important + } + + .pt-md-2, .py-md-2 { + padding-top: .5rem !important + } + + .pr-md-2, .px-md-2 { + padding-right: .5rem !important + } + + .pb-md-2, .py-md-2 { + padding-bottom: .5rem !important + } + + .pl-md-2, .px-md-2 { + padding-left: .5rem !important + } + + .p-md-3 { + padding: 1rem !important + } + + .pt-md-3, .py-md-3 { + padding-top: 1rem !important + } + + .pr-md-3, .px-md-3 { + padding-right: 1rem !important + } + + .pb-md-3, .py-md-3 { + padding-bottom: 1rem !important + } + + .pl-md-3, .px-md-3 { + padding-left: 1rem !important + } + + .p-md-4 { + padding: 1.5rem !important + } + + .pt-md-4, .py-md-4 { + padding-top: 1.5rem !important + } + + .pr-md-4, .px-md-4 { + padding-right: 1.5rem !important + } + + .pb-md-4, .py-md-4 { + padding-bottom: 1.5rem !important + } + + .pl-md-4, .px-md-4 { + padding-left: 1.5rem !important + } + + .p-md-5 { + padding: 3rem !important + } + + .pt-md-5, .py-md-5 { + padding-top: 3rem !important + } + + .pr-md-5, .px-md-5 { + padding-right: 3rem !important + } + + .pb-md-5, .py-md-5 { + padding-bottom: 3rem !important + } + + .pl-md-5, .px-md-5 { + padding-left: 3rem !important + } + + .m-md-n1 { + margin: -.25rem !important + } + + .mt-md-n1, .my-md-n1 { + margin-top: -.25rem !important + } + + .mr-md-n1, .mx-md-n1 { + margin-right: -.25rem !important + } + + .mb-md-n1, .my-md-n1 { + margin-bottom: -.25rem !important + } + + .ml-md-n1, .mx-md-n1 { + margin-left: -.25rem !important + } + + .m-md-n2 { + margin: -.5rem !important + } + + .mt-md-n2, .my-md-n2 { + margin-top: -.5rem !important + } + + .mr-md-n2, .mx-md-n2 { + margin-right: -.5rem !important + } + + .mb-md-n2, .my-md-n2 { + margin-bottom: -.5rem !important + } + + .ml-md-n2, .mx-md-n2 { + margin-left: -.5rem !important + } + + .m-md-n3 { + margin: -1rem !important + } + + .mt-md-n3, .my-md-n3 { + margin-top: -1rem !important + } + + .mr-md-n3, .mx-md-n3 { + margin-right: -1rem !important + } + + .mb-md-n3, .my-md-n3 { + margin-bottom: -1rem !important + } + + .ml-md-n3, .mx-md-n3 { + margin-left: -1rem !important + } + + .m-md-n4 { + margin: -1.5rem !important + } + + .mt-md-n4, .my-md-n4 { + margin-top: -1.5rem !important + } + + .mr-md-n4, .mx-md-n4 { + margin-right: -1.5rem !important + } + + .mb-md-n4, .my-md-n4 { + margin-bottom: -1.5rem !important + } + + .ml-md-n4, .mx-md-n4 { + margin-left: -1.5rem !important + } + + .m-md-n5 { + margin: -3rem !important + } + + .mt-md-n5, .my-md-n5 { + margin-top: -3rem !important + } + + .mr-md-n5, .mx-md-n5 { + margin-right: -3rem !important + } + + .mb-md-n5, .my-md-n5 { + margin-bottom: -3rem !important + } + + .ml-md-n5, .mx-md-n5 { + margin-left: -3rem !important + } + + .m-md-auto { + margin: auto !important + } + + .mt-md-auto, .my-md-auto { + margin-top: auto !important + } + + .mr-md-auto, .mx-md-auto { + margin-right: auto !important + } + + .mb-md-auto, .my-md-auto { + margin-bottom: auto !important + } + + .ml-md-auto, .mx-md-auto { + margin-left: auto !important + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important + } + + .mt-lg-0, .my-lg-0 { + margin-top: 0 !important + } + + .mr-lg-0, .mx-lg-0 { + margin-right: 0 !important + } + + .mb-lg-0, .my-lg-0 { + margin-bottom: 0 !important + } + + .ml-lg-0, .mx-lg-0 { + margin-left: 0 !important + } + + .m-lg-1 { + margin: .25rem !important + } + + .mt-lg-1, .my-lg-1 { + margin-top: .25rem !important + } + + .mr-lg-1, .mx-lg-1 { + margin-right: .25rem !important + } + + .mb-lg-1, .my-lg-1 { + margin-bottom: .25rem !important + } + + .ml-lg-1, .mx-lg-1 { + margin-left: .25rem !important + } + + .m-lg-2 { + margin: .5rem !important + } + + .mt-lg-2, .my-lg-2 { + margin-top: .5rem !important + } + + .mr-lg-2, .mx-lg-2 { + margin-right: .5rem !important + } + + .mb-lg-2, .my-lg-2 { + margin-bottom: .5rem !important + } + + .ml-lg-2, .mx-lg-2 { + margin-left: .5rem !important + } + + .m-lg-3 { + margin: 1rem !important + } + + .mt-lg-3, .my-lg-3 { + margin-top: 1rem !important + } + + .mr-lg-3, .mx-lg-3 { + margin-right: 1rem !important + } + + .mb-lg-3, .my-lg-3 { + margin-bottom: 1rem !important + } + + .ml-lg-3, .mx-lg-3 { + margin-left: 1rem !important + } + + .m-lg-4 { + margin: 1.5rem !important + } + + .mt-lg-4, .my-lg-4 { + margin-top: 1.5rem !important + } + + .mr-lg-4, .mx-lg-4 { + margin-right: 1.5rem !important + } + + .mb-lg-4, .my-lg-4 { + margin-bottom: 1.5rem !important + } + + .ml-lg-4, .mx-lg-4 { + margin-left: 1.5rem !important + } + + .m-lg-5 { + margin: 3rem !important + } + + .mt-lg-5, .my-lg-5 { + margin-top: 3rem !important + } + + .mr-lg-5, .mx-lg-5 { + margin-right: 3rem !important + } + + .mb-lg-5, .my-lg-5 { + margin-bottom: 3rem !important + } + + .ml-lg-5, .mx-lg-5 { + margin-left: 3rem !important + } + + .p-lg-0 { + padding: 0 !important + } + + .pt-lg-0, .py-lg-0 { + padding-top: 0 !important + } + + .pr-lg-0, .px-lg-0 { + padding-right: 0 !important + } + + .pb-lg-0, .py-lg-0 { + padding-bottom: 0 !important + } + + .pl-lg-0, .px-lg-0 { + padding-left: 0 !important + } + + .p-lg-1 { + padding: .25rem !important + } + + .pt-lg-1, .py-lg-1 { + padding-top: .25rem !important + } + + .pr-lg-1, .px-lg-1 { + padding-right: .25rem !important + } + + .pb-lg-1, .py-lg-1 { + padding-bottom: .25rem !important + } + + .pl-lg-1, .px-lg-1 { + padding-left: .25rem !important + } + + .p-lg-2 { + padding: .5rem !important + } + + .pt-lg-2, .py-lg-2 { + padding-top: .5rem !important + } + + .pr-lg-2, .px-lg-2 { + padding-right: .5rem !important + } + + .pb-lg-2, .py-lg-2 { + padding-bottom: .5rem !important + } + + .pl-lg-2, .px-lg-2 { + padding-left: .5rem !important + } + + .p-lg-3 { + padding: 1rem !important + } + + .pt-lg-3, .py-lg-3 { + padding-top: 1rem !important + } + + .pr-lg-3, .px-lg-3 { + padding-right: 1rem !important + } + + .pb-lg-3, .py-lg-3 { + padding-bottom: 1rem !important + } + + .pl-lg-3, .px-lg-3 { + padding-left: 1rem !important + } + + .p-lg-4 { + padding: 1.5rem !important + } + + .pt-lg-4, .py-lg-4 { + padding-top: 1.5rem !important + } + + .pr-lg-4, .px-lg-4 { + padding-right: 1.5rem !important + } + + .pb-lg-4, .py-lg-4 { + padding-bottom: 1.5rem !important + } + + .pl-lg-4, .px-lg-4 { + padding-left: 1.5rem !important + } + + .p-lg-5 { + padding: 3rem !important + } + + .pt-lg-5, .py-lg-5 { + padding-top: 3rem !important + } + + .pr-lg-5, .px-lg-5 { + padding-right: 3rem !important + } + + .pb-lg-5, .py-lg-5 { + padding-bottom: 3rem !important + } + + .pl-lg-5, .px-lg-5 { + padding-left: 3rem !important + } + + .m-lg-n1 { + margin: -.25rem !important + } + + .mt-lg-n1, .my-lg-n1 { + margin-top: -.25rem !important + } + + .mr-lg-n1, .mx-lg-n1 { + margin-right: -.25rem !important + } + + .mb-lg-n1, .my-lg-n1 { + margin-bottom: -.25rem !important + } + + .ml-lg-n1, .mx-lg-n1 { + margin-left: -.25rem !important + } + + .m-lg-n2 { + margin: -.5rem !important + } + + .mt-lg-n2, .my-lg-n2 { + margin-top: -.5rem !important + } + + .mr-lg-n2, .mx-lg-n2 { + margin-right: -.5rem !important + } + + .mb-lg-n2, .my-lg-n2 { + margin-bottom: -.5rem !important + } + + .ml-lg-n2, .mx-lg-n2 { + margin-left: -.5rem !important + } + + .m-lg-n3 { + margin: -1rem !important + } + + .mt-lg-n3, .my-lg-n3 { + margin-top: -1rem !important + } + + .mr-lg-n3, .mx-lg-n3 { + margin-right: -1rem !important + } + + .mb-lg-n3, .my-lg-n3 { + margin-bottom: -1rem !important + } + + .ml-lg-n3, .mx-lg-n3 { + margin-left: -1rem !important + } + + .m-lg-n4 { + margin: -1.5rem !important + } + + .mt-lg-n4, .my-lg-n4 { + margin-top: -1.5rem !important + } + + .mr-lg-n4, .mx-lg-n4 { + margin-right: -1.5rem !important + } + + .mb-lg-n4, .my-lg-n4 { + margin-bottom: -1.5rem !important + } + + .ml-lg-n4, .mx-lg-n4 { + margin-left: -1.5rem !important + } + + .m-lg-n5 { + margin: -3rem !important + } + + .mt-lg-n5, .my-lg-n5 { + margin-top: -3rem !important + } + + .mr-lg-n5, .mx-lg-n5 { + margin-right: -3rem !important + } + + .mb-lg-n5, .my-lg-n5 { + margin-bottom: -3rem !important + } + + .ml-lg-n5, .mx-lg-n5 { + margin-left: -3rem !important + } + + .m-lg-auto { + margin: auto !important + } + + .mt-lg-auto, .my-lg-auto { + margin-top: auto !important + } + + .mr-lg-auto, .mx-lg-auto { + margin-right: auto !important + } + + .mb-lg-auto, .my-lg-auto { + margin-bottom: auto !important + } + + .ml-lg-auto, .mx-lg-auto { + margin-left: auto !important + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important + } + + .mt-xl-0, .my-xl-0 { + margin-top: 0 !important + } + + .mr-xl-0, .mx-xl-0 { + margin-right: 0 !important + } + + .mb-xl-0, .my-xl-0 { + margin-bottom: 0 !important + } + + .ml-xl-0, .mx-xl-0 { + margin-left: 0 !important + } + + .m-xl-1 { + margin: .25rem !important + } + + .mt-xl-1, .my-xl-1 { + margin-top: .25rem !important + } + + .mr-xl-1, .mx-xl-1 { + margin-right: .25rem !important + } + + .mb-xl-1, .my-xl-1 { + margin-bottom: .25rem !important + } + + .ml-xl-1, .mx-xl-1 { + margin-left: .25rem !important + } + + .m-xl-2 { + margin: .5rem !important + } + + .mt-xl-2, .my-xl-2 { + margin-top: .5rem !important + } + + .mr-xl-2, .mx-xl-2 { + margin-right: .5rem !important + } + + .mb-xl-2, .my-xl-2 { + margin-bottom: .5rem !important + } + + .ml-xl-2, .mx-xl-2 { + margin-left: .5rem !important + } + + .m-xl-3 { + margin: 1rem !important + } + + .mt-xl-3, .my-xl-3 { + margin-top: 1rem !important + } + + .mr-xl-3, .mx-xl-3 { + margin-right: 1rem !important + } + + .mb-xl-3, .my-xl-3 { + margin-bottom: 1rem !important + } + + .ml-xl-3, .mx-xl-3 { + margin-left: 1rem !important + } + + .m-xl-4 { + margin: 1.5rem !important + } + + .mt-xl-4, .my-xl-4 { + margin-top: 1.5rem !important + } + + .mr-xl-4, .mx-xl-4 { + margin-right: 1.5rem !important + } + + .mb-xl-4, .my-xl-4 { + margin-bottom: 1.5rem !important + } + + .ml-xl-4, .mx-xl-4 { + margin-left: 1.5rem !important + } + + .m-xl-5 { + margin: 3rem !important + } + + .mt-xl-5, .my-xl-5 { + margin-top: 3rem !important + } + + .mr-xl-5, .mx-xl-5 { + margin-right: 3rem !important + } + + .mb-xl-5, .my-xl-5 { + margin-bottom: 3rem !important + } + + .ml-xl-5, .mx-xl-5 { + margin-left: 3rem !important + } + + .p-xl-0 { + padding: 0 !important + } + + .pt-xl-0, .py-xl-0 { + padding-top: 0 !important + } + + .pr-xl-0, .px-xl-0 { + padding-right: 0 !important + } + + .pb-xl-0, .py-xl-0 { + padding-bottom: 0 !important + } + + .pl-xl-0, .px-xl-0 { + padding-left: 0 !important + } + + .p-xl-1 { + padding: .25rem !important + } + + .pt-xl-1, .py-xl-1 { + padding-top: .25rem !important + } + + .pr-xl-1, .px-xl-1 { + padding-right: .25rem !important + } + + .pb-xl-1, .py-xl-1 { + padding-bottom: .25rem !important + } + + .pl-xl-1, .px-xl-1 { + padding-left: .25rem !important + } + + .p-xl-2 { + padding: .5rem !important + } + + .pt-xl-2, .py-xl-2 { + padding-top: .5rem !important + } + + .pr-xl-2, .px-xl-2 { + padding-right: .5rem !important + } + + .pb-xl-2, .py-xl-2 { + padding-bottom: .5rem !important + } + + .pl-xl-2, .px-xl-2 { + padding-left: .5rem !important + } + + .p-xl-3 { + padding: 1rem !important + } + + .pt-xl-3, .py-xl-3 { + padding-top: 1rem !important + } + + .pr-xl-3, .px-xl-3 { + padding-right: 1rem !important + } + + .pb-xl-3, .py-xl-3 { + padding-bottom: 1rem !important + } + + .pl-xl-3, .px-xl-3 { + padding-left: 1rem !important + } + + .p-xl-4 { + padding: 1.5rem !important + } + + .pt-xl-4, .py-xl-4 { + padding-top: 1.5rem !important + } + + .pr-xl-4, .px-xl-4 { + padding-right: 1.5rem !important + } + + .pb-xl-4, .py-xl-4 { + padding-bottom: 1.5rem !important + } + + .pl-xl-4, .px-xl-4 { + padding-left: 1.5rem !important + } + + .p-xl-5 { + padding: 3rem !important + } + + .pt-xl-5, .py-xl-5 { + padding-top: 3rem !important + } + + .pr-xl-5, .px-xl-5 { + padding-right: 3rem !important + } + + .pb-xl-5, .py-xl-5 { + padding-bottom: 3rem !important + } + + .pl-xl-5, .px-xl-5 { + padding-left: 3rem !important + } + + .m-xl-n1 { + margin: -.25rem !important + } + + .mt-xl-n1, .my-xl-n1 { + margin-top: -.25rem !important + } + + .mr-xl-n1, .mx-xl-n1 { + margin-right: -.25rem !important + } + + .mb-xl-n1, .my-xl-n1 { + margin-bottom: -.25rem !important + } + + .ml-xl-n1, .mx-xl-n1 { + margin-left: -.25rem !important + } + + .m-xl-n2 { + margin: -.5rem !important + } + + .mt-xl-n2, .my-xl-n2 { + margin-top: -.5rem !important + } + + .mr-xl-n2, .mx-xl-n2 { + margin-right: -.5rem !important + } + + .mb-xl-n2, .my-xl-n2 { + margin-bottom: -.5rem !important + } + + .ml-xl-n2, .mx-xl-n2 { + margin-left: -.5rem !important + } + + .m-xl-n3 { + margin: -1rem !important + } + + .mt-xl-n3, .my-xl-n3 { + margin-top: -1rem !important + } + + .mr-xl-n3, .mx-xl-n3 { + margin-right: -1rem !important + } + + .mb-xl-n3, .my-xl-n3 { + margin-bottom: -1rem !important + } + + .ml-xl-n3, .mx-xl-n3 { + margin-left: -1rem !important + } + + .m-xl-n4 { + margin: -1.5rem !important + } + + .mt-xl-n4, .my-xl-n4 { + margin-top: -1.5rem !important + } + + .mr-xl-n4, .mx-xl-n4 { + margin-right: -1.5rem !important + } + + .mb-xl-n4, .my-xl-n4 { + margin-bottom: -1.5rem !important + } + + .ml-xl-n4, .mx-xl-n4 { + margin-left: -1.5rem !important + } + + .m-xl-n5 { + margin: -3rem !important + } + + .mt-xl-n5, .my-xl-n5 { + margin-top: -3rem !important + } + + .mr-xl-n5, .mx-xl-n5 { + margin-right: -3rem !important + } + + .mb-xl-n5, .my-xl-n5 { + margin-bottom: -3rem !important + } + + .ml-xl-n5, .mx-xl-n5 { + margin-left: -3rem !important + } + + .m-xl-auto { + margin: auto !important + } + + .mt-xl-auto, .my-xl-auto { + margin-top: auto !important + } + + .mr-xl-auto, .mx-xl-auto { + margin-right: auto !important + } + + .mb-xl-auto, .my-xl-auto { + margin-bottom: auto !important + } + + .ml-xl-auto, .mx-xl-auto { + margin-left: auto !important + } +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important +} + +.text-justify { + text-align: justify !important +} + +.text-wrap { + white-space: normal !important +} + +.text-nowrap { + white-space: nowrap !important +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap +} + +.text-left { + text-align: left !important +} + +.text-right { + text-align: right !important +} + +.text-center { + text-align: center !important +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important + } + + .text-sm-right { + text-align: right !important + } + + .text-sm-center { + text-align: center !important + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important + } + + .text-md-right { + text-align: right !important + } + + .text-md-center { + text-align: center !important + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important + } + + .text-lg-right { + text-align: right !important + } + + .text-lg-center { + text-align: center !important + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important + } + + .text-xl-right { + text-align: right !important + } + + .text-xl-center { + text-align: center !important + } +} + +.text-lowercase { + text-transform: lowercase !important +} + +.text-uppercase { + text-transform: uppercase !important +} + +.text-capitalize { + text-transform: capitalize !important +} + +.font-weight-light { + font-weight: 300 !important +} + +.font-weight-lighter { + font-weight: lighter !important +} + +.font-weight-normal { + font-weight: 400 !important +} + +.font-weight-bold { + font-weight: 700 !important +} + +.font-weight-bolder { + font-weight: bolder !important +} + +.font-italic { + font-style: italic !important +} + +.text-white { + color: #fff !important +} + +.text-primary { + color: #007bff !important +} + +a.text-primary:focus, a.text-primary:hover { + color: #0056b3 !important +} + +.text-secondary { + color: #6c757d !important +} + +a.text-secondary:focus, a.text-secondary:hover { + color: #494f54 !important +} + +.text-success { + color: #28a745 !important +} + +a.text-success:focus, a.text-success:hover { + color: #19692c !important +} + +.text-info { + color: #17a2b8 !important +} + +a.text-info:focus, a.text-info:hover { + color: #0f6674 !important +} + +.text-warning { + color: #ffc107 !important +} + +a.text-warning:focus, a.text-warning:hover { + color: #ba8b00 !important +} + +.text-danger { + color: #dc3545 !important +} + +a.text-danger:focus, a.text-danger:hover { + color: #a71d2a !important +} + +.text-light { + color: #f8f9fa !important +} + +a.text-light:focus, a.text-light:hover { + color: #cbd3da !important +} + +.text-dark { + color: #343a40 !important +} + +a.text-dark:focus, a.text-dark:hover { + color: #121416 !important +} + +.text-body { + color: #212529 !important +} + +.text-muted { + color: #6c757d !important +} + +.text-black-50 { + color: rgba(0, 0, 0, .5) !important +} + +.text-white-50 { + color: rgba(255, 255, 255, .5) !important +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0 +} + +.text-decoration-none { + text-decoration: none !important +} + +.text-break { + word-break: break-word !important; + overflow-wrap: break-word !important +} + +.text-reset { + color: inherit !important +} + +.visible { + visibility: visible !important +} + +.invisible { + visibility: hidden !important +} + +@media print { + *, ::after, ::before { + text-shadow: none !important; + box-shadow: none !important + } + + a:not(.btn) { + text-decoration: underline + } + + abbr[title]::after { + content: " (" attr(title) ")" + } + + pre { + white-space: pre-wrap !important + } + + blockquote, pre { + border: 1px solid #adb5bd; + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + img, tr { + page-break-inside: avoid + } + + h2, h3, p { + orphans: 3; + widows: 3 + } + + h2, h3 { + page-break-after: avoid + } + + @page { + size: a3 + } + + body { + min-width: 992px !important + } + + .container { + min-width: 992px !important + } + + .navbar { + display: none + } + + .badge { + border: 1px solid #000 + } + + .table { + border-collapse: collapse !important + } + + .table td, .table th { + background-color: #fff !important + } + + .table-bordered td, .table-bordered th { + border: 1px solid #dee2e6 !important + } + + .table-dark { + color: inherit + } + + .table-dark tbody + tbody, .table-dark td, .table-dark th, .table-dark thead th { + border-color: #dee2e6 + } + + .table .thead-dark th { + color: inherit; + border-color: #dee2e6 + } +} + +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/images/icon-information.svg b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/images/icon-information.svg new file mode 100644 index 00000000000..ef2c9a375ab --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/images/icon-information.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/style.css b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/style.css new file mode 100644 index 00000000000..802e3730d64 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/css/style.css @@ -0,0 +1,228 @@ +body { + background-color: white; +} + +th { + color: grey; + font-weight: normal; +} + +#main-container { + background-color: white; +} + +.dropdown-item { + display: block; + width: 100%; + padding: .25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0 +} + +.dropdown-item:focus, .dropdown-item:hover { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent +} + +.form-container { + border: 1px solid #dfdada; + border-radius: 8px 8px 8px 8px; + padding: 10px; +} + +.clickable { + cursor: pointer; +} + +.success-color { + color: #5CB85C; + font-weight: bold; +} + +.error-color { + color: #ff0000; + font-weight: bold; +} + +.notifications { + border: 1px solid #dfdada; + padding-top: 5px; + padding-bottom: 5px; +} + +.is-invalid input { + border-color: red; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25) +} + +.float-right { + float: right; +} + +.sector-map-container { + min-height: 82vh; +} + +.custom-panel > span { + margin-top: 10px; +} + +.button-wrapper { + margin-top: 0 !important; +} + +.footer { + position: relative !important; +} + +.title { + text-align: center; + padding-bottom: 30px; +} +.section-title { + text-align: center; +} + +.section-sub-title { + text-align: center; + padding-bottom: 10px; + font-size: 20px; + margin: auto; + width: 70%; +} +.sectors-table { + width: 100%; +} + +.sectors-table td { + padding-right: 1%; + padding-bottom: 1%; +} + +.margin_2 { + margin: 2px; +} + +.insert-data-text { + color: green; +} + +.add-new-text { + padding-left: 5px; +} + +.required-fields { + color: #ff0000; +} + +.nav { + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none +} + +.nav-link { + display: block; + padding: .5rem 1rem; + background-color: #dee2e6; +} + +.nav-link:focus, .nav-link:hover { + text-decoration: none +} + +.nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default +} + +.nav-tabs { + border-bottom: none; +} + +.nav-tabs .nav-item { + margin-bottom: -2px +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem +} + +.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover { + border-color: #e9ecef #e9ecef #dee2e6 +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent +} + +.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active { + color: black; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.info-icon { + height: 15px; + width: 15px; + float: left; + margin-right: 3px; + top: 2px; + position: relative; +} + +.tooltip { + opacity: 1; +} + +.tab-content { + border: 1px solid #dee2e6; + display: grid; +} + +.tab-pane { + margin-bottom: 20px; +} + +.loading { + margin: 25px; + font-size: 2em; + color: #6c757d; +} + +.actions-column { + width: 5%; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json new file mode 100644 index 00000000000..bda7b051f92 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json @@ -0,0 +1,17 @@ +{ + "amp.admin.sectorMapping:page-title": "Aid Management Platform - Sector Mapping Manager", + "amp.admin.sectorMapping:menu-item-sectors-mapping": "Sectors Mapping", + "amp.admin.sectorMapping:mappings-manager": "Sector Mapping Manager", + "amp.admin.sectorMapping:title": "Sectors Mapping", + "amp.admin.sectorMapping:sub-title": "This module manages the mappings between primary and secondary sectors.", + "amp.admin.sectorMapping:required-fields": "Required Fields", + "amp.admin.sectorMapping:loading": "Loading...", + "amp.admin.sectorMapping:actions": "Actions", + "amp.admin.sectorMapping:add-new": "Add New", + "amp.admin.sectorMapping:button-save-all-edits": "Save all edits", + "amp.admin.sectorMapping:button-revert-all-edits": "Revert all changes", + "amp.admin.sectorMapping:insert-data": "(insert data to the new field)", + "amp.admin.sectorMapping:click-save": "Click the Save icon to save the added data row", + "amp.admin.sectorMapping:confirm-remove-row": "Are you sure you want to remove this mapping? (remember to click Save All button to confirm your changes).", + "amp.admin.sectorMapping:no-matches-found": "No matches found" + } diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx new file mode 100644 index 00000000000..f6c002cdcea --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx @@ -0,0 +1,21 @@ +//export const SRC_PROGRAM = 'src-program'; +export const SRC_SCHEME_SECTOR = 'src-scheme-sector'; +export const SRC_SECTOR = 'src-sector'; +//export const DST_PROGRAM = 'dst-program'; +export const DST_SCHEME_SECTOR = 'dst-scheme-sector'; +export const DST_SECTOR = 'dst-sector'; +//export const ALL_PROGRAMS = 'all-programs'; +export const ALL_SCHEMES = 'all-schemes'; +export const ALL_SECTORS = 'all-sectors'; +//export const PROGRAM_MAPPING = 'program-mapping'; +export const SECTOR_MAPPING = 'sector-mapping'; +//export const PROGRAM = '-program'; +export const SECTOR = '-program'; +export const VALUE = 'value'; +export const CHILDREN = 'children'; +export const STATE_LEVEL_FIELD = 'srcLvl'; +export const TYPE_SRC = 'src'; +export const TYPE_DST = 'dst'; +export const LAYOUT_EP = '/rest/security/layout'; +export const SETTINGS_EP = '/rest/amp/settings'; +//export const UPDATE_ACTIVITIES_EP = '/rest/activity/updateMappings/async'; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js new file mode 100644 index 00000000000..5f79d3ca3a4 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; +import { applyMiddleware, compose, createStore } from 'redux'; +import thunk from 'redux-thunk'; +import { Provider } from 'react-redux'; +import { Container, Col, Row } from 'react-bootstrap'; +import rootReducer from './reducers/rootReducer'; +import defaultTrnPack from './config/initialTranslations.json'; +import Startup from './components/Startup'; +import SectorMappingAdminNavigator from './components/SectorMappingAdminNavigator'; +// eslint-disable-next-line no-unused-vars +import style from './components/css/style.css'; + +const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; + +class AdminSectorMappingApp extends Component { + constructor(props) { + super(props); + this.store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk))); + } + + render() { + return ( + + + + + + + + + + + + ); + } +} + +export default AdminSectorMappingApp; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js new file mode 100644 index 00000000000..5f869a2c3a6 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js @@ -0,0 +1,12 @@ +import { combineReducers } from 'redux'; +import startupReducer from './startupReducer'; +import saveSectorMappingReducer from './saveSectorMappingReducer'; +import translationsReducer from '../../../../utils/reducers/translationsReducer'; +//import updateActivitiesReducer from './updateActivitiesReducer'; + +export default combineReducers({ + startupReducer, + translationsReducer, + saveSectorMappingReducer + //updateActivitiesReducer +}); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js new file mode 100644 index 00000000000..efc7c2f7dc7 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js @@ -0,0 +1,38 @@ +import { SAVE_SECTOR_MAP_SUCCESS, SAVE_SECTOR_MAP_PENDING, SAVE_SECTOR_MAP_ERROR } from '../actions/saveAction'; + +const initialState = { + saving: false, + //NDDs: [], + SectorMappings: [], + error: null +}; + +export default function sendSectorMappingReducer(state = initialState, action) { + switch (action.type) { + case SAVE_SECTOR_MAP_PENDING: + return { + ...state, + saving: true + }; + case SAVE_SECTOR_MAP_SUCCESS: + return { + ...state, + saving: false, + data: action.payload + }; + case SAVE_SECTOR_MAP_ERROR: + return { + ...state, + saving: false, + error: action.error + }; + default: + return state; + } +} + +export const sendSectorMapping = state => state.data; +export const sendSectorMappingPending = state => state.saving; +export const sendSectorMappingError = state => state.error; +export const sendSectorMappingSaving = state => state.saving; + diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js new file mode 100644 index 00000000000..afec98f3057 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js @@ -0,0 +1,68 @@ +import { + FETCH_SECTOR_MAP_SUCCESS, + FETCH_SECTOR_MAP_PENDING, + FETCH_SECTOR_MAP_ERROR, + FETCH_SECTORS_SUCCESS, + FETCH_SECTORS_PENDING, + FETCH_SECTORS_ERROR, + FETCH_SCHEMES_PENDING, + FETCH_SCHEMES_SUCCESS, + FETCH_SCHEMES_ERROR +} from '../actions/startupAction'; + +const initialState = { + pendingSectorMapping: false, // pendingNDD + pendingSchemes: false, // pendingPrograms + sectorMappings: [], //NDDs + error: null, + schemes: [] //programs +}; + +export default function startupReducer(state = initialState, action) { + switch (action.type) { + case FETCH_SECTOR_MAP_PENDING: + return { + ...state, + pendingSectorMapping: true + }; + case FETCH_SECTOR_MAP_SUCCESS: + return { + ...state, + pendingSectorMapping: false, + sectorMappings: action.payload + }; + case FETCH_SECTOR_MAP_ERROR: + return { + ...state, + pendingSectorMapping: false, + error: action.error + }; + + case FETCH_SCHEMES_PENDING: + return { + ...state, + pendingSchemes: true + }; + case FETCH_SCHEMES_SUCCESS: + return { + ...state, + pendingSchemes: false, + schemes: action.payload + }; + case FETCH_SCHEMES_ERROR: + return { + ...state, + pendingSchemes: false, + error: action.error + }; + default: + return state; + } +} + +export const getSectorMappings = state => state.sectorMappings; +export const getSectorMappingPending = state => state.pendingSectorMapping; +export const getSectorMappingError = state => state.error; +export const getSchemes = state => state.schemes; +export const getSchemesPending = state => state.pendingSchemes +export const getSchemesError = state => state.error; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json new file mode 100644 index 00000000000..864c03c3752 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json @@ -0,0 +1,11 @@ +{ + "amp.admin.sectorMapping:title": "Sectors Mapping", + "amp.admin.sectorMapping:tooltip-direct-sector": "The main sector in use. What is selected in the Activity Form for the Source Sector will limit what options are available in the Destination Sector drop down.", + "amp.admin.sectorMapping:tooltip-indirect-sector": "The indirect sector is mapped against your reference sector. If your reference sector is your national development strategy, the indirect sector could be any regional/international strategy (ex. SDGs, New Deal, etc.) or vice-versa. Please note that the indirect sector will be displayed in the inner ring of the dashboard. The assigned percentage will be automatically distributed. This sector will not be seen in the activity form.", + "amp.admin.sectorMapping:choose_main_src_sector": "Choose a Source Sector", + "amp.admin.sectorMapping:choose_main_dst_sector": "Choose a Destination Sector", + "amp.admin.sectorMapping:src-sector-lvl-1": "Source Sector", + "amp.admin.sectorMapping:dst-sector-lvl-1": "Destination Sector", + "amp.admin.sectorMapping:warning_on_change_main_sector": "If you change the Source Sector or the Destination Sector the existing mappings will be deleted, are you sure you want to continue?", + "amp.admin.sectorMapping:notification-saved-ok": "All mappings saved correctly." +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js new file mode 100644 index 00000000000..8f0440eca78 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js @@ -0,0 +1,77 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { + applyMiddleware, bindActionCreators, compose, createStore +} from 'redux'; +import thunk from 'redux-thunk'; +import { connect, Provider } from 'react-redux'; + +import Main from '../components/Main'; +import rootReducer from '../reducers/rootReducer'; +import defaultTrnPack from './config/initialTranslations.json'; +import Startup, {SectorMappingContext} from '../components/Startup'; + +const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; + +const API = { + // mappingConfig: '/rest/ndd/indirect-programs-mapping-config', + // mappingSave: '/rest/ndd/indirect-programs-mapping', + // programs: '/rest/ndd/available-indirect-programs', + // programsSave: '/rest/ndd/update-source-destination-indirect-programs', + + mappingConfig: '/rest/sectors-mapping/sectors-mapping-config', + allMappings: '/rest/sectors-mapping/all-mappings', + allSchemes: '/rest/sectors-mapping/all-schemes', + sectorsClassified: '/rest/sectors-mapping/sectors-classified/', + sectorsByScheme: '/rest/sectors-mapping/sectors-by-scheme/', + baseEndpoint: '/rest/sectors-mapping' // Create (POST) and Delete (DELETE) endpoints +}; + +const TRN_PREFIX = 'amp.admin.sectorMapping:'; + +class AdminSectorMappingApp extends Component { + constructor(props) { + super(props); + this.store = createStore(rootReducer, composeEnhancer(applyMiddleware(thunk))); + } + + render() { + // const { translations } = this.context; + // const { selected } = this.props; + // return ( + // selected + // ? ( + // + // + //

Admin Sector Mapping (Index 2°)

+ //

{translations[`${TRN_PREFIX}title`]}

+ //
{translations[`${TRN_PREFIX}sub-title`]}
+ // {/*
*/} + // + // + // ) : null); + + const { translations } = this.context; + return ( + + +

{translations[`${TRN_PREFIX}title`]}

+
{translations[`${TRN_PREFIX}sub-title`]}
+
+ + + ); + } +} + +AdminSectorMappingApp.contextType = SectorMappingContext; + +AdminSectorMappingApp.propTypes = { + selected: PropTypes.bool.isRequired +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(AdminSectorMappingApp); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java index 7749b483cca..3b9cb0b9ec6 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java @@ -4,6 +4,10 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.digijava.kernel.ampapi.endpoints.gpi.ValidationUtils; +import org.digijava.kernel.ampapi.endpoints.ndd.IndirectProgramMappingConfiguration; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.GenericSelectObjDTO; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.MappingConfigurationDTO; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.SchemaClassificationDTO; import org.digijava.kernel.ampapi.endpoints.security.AuthRule; import org.digijava.kernel.ampapi.endpoints.util.ApiMethod; import org.digijava.kernel.exception.DgException; @@ -24,6 +28,15 @@ public class SectorMappingEndpoints { private final SectorMappingService smService = new SectorMappingService(); + @GET + @Path("sectors-mapping-config") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(id = "getMappingsConf") + @ApiOperation("Returns configuration saved for sectors mapping.") + public MappingConfigurationDTO getMappingsConf() { + return smService.getMappingsConf(); + } + @GET @Path("all-mappings") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") @@ -34,23 +47,21 @@ public Collection getAllSectorMappings() { } @GET - @Path("sectors-classified/{classSector}") + @Path("all-schemes") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") @ApiMethod(id = "getClassifiedSectors") - @ApiOperation("Returns primary or secondary sectors by parameter. 1: Primary, 2: Secondary") - public List getClassifiedSectors(@ApiParam("Property value") @PathParam("classSector") Long classSector) { - List paramValuesValid = Arrays.asList(1L, 2L); - ValidationUtils.valuesValid(paramValuesValid, classSector); - return smService.getClassifiedSectors(classSector); + @ApiOperation("Returns all schemes and his classifications.") + public List getAllSchemes() { + return smService.getAllSchemes(); } @GET - @Path("secondaries-by-primary/{primarySectorId}") + @Path("sectors-by-scheme/{schemeId}") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") - @ApiMethod(id = "getSecondarySectorsByPrimary") - @ApiOperation("Returns a list of secondary sectors by primary sector id.") - public List getSecondarySectorsByPrimary(@ApiParam("Property value") @PathParam("primarySectorId") Long primarySectorId) { - return smService.getSecondSectorsByPrimary(primarySectorId); + @ApiMethod(id = "getClassifiedSectors") + @ApiOperation("Returns a list of sectors level 1 by scheme id.") + public List getSectorsByScheme(@ApiParam("Property value") @PathParam("schemeId") Long schemeId) { + return smService.getSectorsByScheme(schemeId); } @POST diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index fbb554d8adf..b151de415ba 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -2,12 +2,15 @@ import org.apache.log4j.Logger; import org.digijava.kernel.ampapi.endpoints.common.values.ValueConverter; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.GenericSelectObjDTO; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.MappingConfigurationDTO; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.SchemaClassificationDTO; import org.digijava.kernel.exception.DgException; import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.util.SectorUtil; -import java.io.Serializable; import java.util.*; +import java.util.stream.Collectors; /** * @author Diego Rossi @@ -24,50 +27,11 @@ private String getSectorClassById(Long id) { return sectorClasses.get(id); } - public static class SingleSectorData implements Serializable { - private Long id; - private String value; - private Boolean isPrimary; + public List getSectorsByScheme(final Long schemeId) { + List sectors = new ArrayList<>(); - SingleSectorData(Long id, String value, Boolean isPrimary) { - this.id = id; - this.value = value; - this.isPrimary = isPrimary; - } - - public Long getId() { - return id; - } - - public String getValue() { - return value; - } - - public Boolean getIsPrimary() { - return isPrimary; - } - } - - /** - * Returns a list of primary or secondary sectors by parameter. - */ - public List getClassifiedSectors(final Long classSector) { - List sectors = new ArrayList<>(); - - String sectorClass = getSectorClassById(classSector); - - if (sectorClass == null) { - LOGGER.error("Invalid sector class: " + classSector); - return sectors; - } - - List alClassConfig = SectorUtil.getAllClassificationConfigs(); - AmpClassificationConfiguration classConfig = findByName(alClassConfig, sectorClass); - - Long schemeId = classConfig.getClassification().getAmpSecSchemeId(); - Boolean isPrimary = classSector == 1L; SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { - sectors.add(new SingleSectorData(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName(), isPrimary)); + sectors.add(new GenericSelectObjDTO(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName())); }); return sectors; @@ -77,18 +41,8 @@ public Collection getAllSectorMappings() { return SectorUtil.getAllSectorMappings(); } - public List getSecondSectorsByPrimary(Long idPrimary) { - List secondaries = new ArrayList<>(); - Collection mappings = SectorUtil.getSectorMappingsByPrimary(idPrimary); - - if (mappings != null) { - mappings.forEach(mapping -> { - AmpSectorMapping sectorMapping = (AmpSectorMapping) mapping; - AmpSector sector = sectorMapping.getDstSector(); - secondaries.add(new SingleSectorData(sector.getAmpSectorId(), sector.getName(), false)); - }); - } - return secondaries; + public List getAllSchemes() { + return SectorUtil.getAllSectorSchemesAndClassification(); } public void createSectorsMapping(AmpSectorMapping mapping) throws DgException { @@ -99,6 +53,38 @@ public void deleteSectorMapping(Long id) throws DgException { SectorUtil.deleteSectorMapping(id); } + public MappingConfigurationDTO getMappingsConf() { + MappingConfigurationDTO mappingConf = new MappingConfigurationDTO(); + + // Schemes with children + List schemes = SectorUtil.getAllSectorSchemesAndClassification(); + schemes.forEach(schemaClassif -> { + schemaClassif.children = getSectorsByScheme(schemaClassif.id); + }); + mappingConf.allSchemes = schemes; + + // All Mappings + mappingConf.sectorMapping = SectorUtil.getAllSectorMappings(); + if (mappingConf.sectorMapping != null && !mappingConf.sectorMapping.isEmpty()) { + AmpSector srcSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getSrcSector(); + AmpSector dstSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getDstSector(); + + Long srcSchemeId = srcSector.getAmpSecSchemeId().getAmpSecSchemeId(); + if (srcSchemeId != null) { + mappingConf.srcSchemeSector = findSchemeById(mappingConf.allSchemes, srcSchemeId); + } + + Long dstSchemeId = dstSector.getAmpSecSchemeId().getAmpSecSchemeId(); + if (dstSchemeId != null) { + mappingConf.dstSchemeSector = findSchemeById(mappingConf.allSchemes, dstSchemeId); + } + } + + return mappingConf; + } + + //region Private methods + private static AmpClassificationConfiguration findByName(List list, String name) { for (AmpClassificationConfiguration item : list) { if (item.getName().equals(name)) { @@ -107,4 +93,17 @@ private static AmpClassificationConfiguration findByName(List list, Long idScheme) { + List filteredSchemes = list.stream() + .filter(scheme -> scheme.id.equals(idScheme)) + .collect(Collectors.toList()); + + if (filteredSchemes != null && !filteredSchemes.isEmpty()) { + SchemaClassificationDTO scheme = filteredSchemes.get(0); + return new GenericSelectObjDTO(scheme.id, scheme.value); + } else return null; + } + + //endregion } diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java new file mode 100644 index 00000000000..b7962f3c6e4 --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java @@ -0,0 +1,23 @@ +package org.digijava.kernel.ampapi.endpoints.sectorMapping.dto; + +import java.util.List; +public class GenericSelectObjDTO { + public Long id; + public String value; + public List children; + + public GenericSelectObjDTO(Long id, String value) { + this.id = id; + this.value = value; + } + + public GenericSelectObjDTO(Long id, String value, List children) { + this.id = id; + this.value = value; + this.children = children; + } + + public GenericSelectObjDTO() { + } + +} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java new file mode 100644 index 00000000000..9df729ef17d --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java @@ -0,0 +1,31 @@ +package org.digijava.kernel.ampapi.endpoints.sectorMapping.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Collection; +import java.util.List; + +public class MappingConfigurationDTO { + + @JsonProperty("src-scheme-sector") + public GenericSelectObjDTO srcSchemeSector; + + @JsonProperty("dst-scheme-sector") + public GenericSelectObjDTO dstSchemeSector; + + @JsonProperty("all-schemes") + public List allSchemes; + + @JsonProperty("sector-mapping") + public Collection sectorMapping; + + public MappingConfigurationDTO(){} + + public MappingConfigurationDTO(GenericSelectObjDTO srcSchemeSector, GenericSelectObjDTO dstSchemeSector, + List allSchemes, Collection sectorMapping) { + this.srcSchemeSector = srcSchemeSector; + this.dstSchemeSector = dstSchemeSector; + this.allSchemes = allSchemes; + this.sectorMapping = sectorMapping; + } +} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/SchemaClassificationDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/SchemaClassificationDTO.java new file mode 100644 index 00000000000..30dc88fd5d2 --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/SchemaClassificationDTO.java @@ -0,0 +1,15 @@ +package org.digijava.kernel.ampapi.endpoints.sectorMapping.dto; + +import java.util.List; + +public class SchemaClassificationDTO { + public Long id; + public String value; + public Long classificationId; + public String classificationName; + public List children; + + public boolean isVisible() { + return classificationId != null; + } +} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index adb05686dd9..f97fb6b17c5 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -21,6 +21,7 @@ import org.dgfoundation.amp.ar.ColumnConstants; import org.dgfoundation.amp.ar.viewfetcher.RsInfo; import org.dgfoundation.amp.ar.viewfetcher.SQLUtils; +import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.SchemaClassificationDTO; import org.digijava.kernel.exception.DgException; import org.digijava.kernel.persistence.PersistenceManager; import org.digijava.module.aim.dbentity.*; @@ -31,8 +32,10 @@ import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.jdbc.Work; +import org.hibernate.transform.Transformers; import org.hibernate.type.IntegerType; import org.hibernate.type.LongType; +import org.hibernate.type.StandardBasicTypes; /** * Utility class for persisting all Sector with Scheme related entities @@ -158,6 +161,34 @@ public static List getAllSectorSchemes(boolean classificationCo } } + public static List getAllSectorSchemesAndClassification() { + String queryString = null; + Session session = null; + Query qry = null; + List result = null; + + try { + session = PersistenceManager.getSession(); + queryString = "SELECT sch.amp_sec_scheme_id id, sch.sec_scheme_name value, " + + " conf.classification_id classificationId, conf.name classificationName " + + " FROM amp_classification_config conf " + + " FULL OUTER JOIN amp_sector_scheme sch ON conf.classification_id = sch.amp_sec_scheme_id "; + + qry = session.createSQLQuery(queryString) + .addScalar("id", StandardBasicTypes.LONG) + .addScalar("value", StandardBasicTypes.STRING) + .addScalar("classificationId", StandardBasicTypes.LONG) + .addScalar("classificationName", StandardBasicTypes.STRING) + .setResultTransformer(Transformers.aliasToBean(SchemaClassificationDTO.class)); + + result = qry.list(); + } catch (Exception ex) { + logger.error("Unable to get Schemes and classification from database " + ex.getMessage()); + ex.printStackTrace(System.out); + } + return result; + } + @SuppressWarnings("unchecked") public static List getAllParentSectors(Long secSchemeId){ try From 4ae3e74c5c7982ca09d8a9b9aa2a403bc323dadc Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Wed, 29 Nov 2023 18:30:37 -0300 Subject: [PATCH 03/85] AMP-30574 - Sector Mappings CRUD Sector Mappings and cleaning code --- .../sectorMapping/actions/layoutAction.js | 22 ---- .../admin/sectorMapping/actions/saveAction.js | 4 +- .../actions/saveSectorMappings.js | 41 ++++++ .../sectorMapping/actions/startupAction.js | 1 - .../sectorMapping/components/FormSectors.jsx | 124 ++++++++++++++---- .../admin/sectorMapping/components/Header.jsx | 11 +- .../components/HeaderActions.jsx | 79 +++++++++++ .../admin/sectorMapping/components/Main.jsx | 15 +-- .../SectorMappingAdminNavigator.jsx | 12 +- .../components/SectorMappingTable.jsx | 76 +++++++++++ .../components/SectorMappingTableRow.jsx | 123 +++++++++++++++++ .../components/SectorsHeader.jsx | 20 ++- .../admin/sectorMapping/components/Select.jsx | 5 +- .../sectorMapping/components/Startup.jsx | 2 +- .../components/common/HelpTooltip.jsx | 3 - .../config/initialTranslations.json | 2 +- .../sectorMapping/constants/Constants.jsx | 8 +- .../src/modules/admin/sectorMapping/index.js | 2 - .../reducers/saveSectorMappingReducer.js | 1 - .../sectorMapping/reducers/startupReducer.js | 11 +- .../sector/config/initialTranslations.json | 29 +++- .../admin/sectorMapping/sector/index.js | 38 +----- .../admin/sectorMapping/utils/Validation.js | 39 ++++++ .../sectorMapping/SectorMappingEndpoints.java | 25 ++-- .../sectorMapping/SectorMappingService.java | 15 +-- .../dto/MappingConfigurationDTO.java | 1 - .../digijava/module/aim/util/SectorUtil.java | 44 +++---- 27 files changed, 546 insertions(+), 207 deletions(-) create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveSectorMappings.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTableRow.jsx create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js index 0a2cf18ac0f..ee41b05a4ae 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/layoutAction.js @@ -1,9 +1,6 @@ export const FETCH_LAYOUT_PENDING = 'FETCH_LAYOUT_PENDING'; export const FETCH_LAYOUT_SUCCESS = 'FETCH_LAYOUT_SUCCESS'; export const FETCH_LAYOUT_ERROR = 'FETCH_LAYOUT_ERROR'; -export const FETCH_SECTORS_PENDING = 'FETCH_SECTORS_PENDING'; -export const FETCH_SECTORS_SUCCESS = 'FETCH_SECTORS_SUCCESS'; -export const FETCH_SECTORS_ERROR = 'FETCH_SECTORS_ERROR'; export function fetchLayoutPending() { return { @@ -23,22 +20,3 @@ export function fetchLayoutError(error) { error }; } - -export function fetchSectorsPending() { - return { - type: FETCH_SECTORS_PENDING - }; -} - -export function fetchSectorsSuccess(sectors) { - return { - type: FETCH_SECTORS_SUCCESS, - payload: sectors - }; -} -export function fetchSectorsError(error) { - return { - type: FETCH_SECTORS_ERROR, - error - }; -} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js index 25255659811..38d19dd496a 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveAction.js @@ -8,10 +8,10 @@ export function saveSectorMappingPending() { }; } -export function saveSectorMappingSuccess(ndd) { +export function saveSectorMappingSuccess(mappings) { return { type: SAVE_SECTOR_MAP_SUCCESS, - payload: ndd + payload: mappings }; } diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveSectorMappings.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveSectorMappings.js new file mode 100644 index 00000000000..41e2620f563 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/saveSectorMappings.js @@ -0,0 +1,41 @@ +import {saveSectorMappingPending, saveSectorMappingSuccess, saveSectorMappingError} from './saveAction'; +import {LAYOUT_EP} from '../constants/Constants'; + +function saveSectorMappings(mappings, urlSave) { + return dispatch => { + dispatch(saveSectorMappingPending()); + fetch(LAYOUT_EP).then(layoutRes => layoutRes.json()).then(data => { + if (data.logged) { + fetch(urlSave, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(mappings) + }).then(response => { + processResponse(response); + dispatch(saveSectorMappingSuccess(response)); + return response; + }) + .catch(error => { + dispatch(saveSectorMappingError(error)); + }); + } else { + window.location.replace('/login.do'); + dispatch(saveSectorMappingError({ error: 'not logged' })); + } + }).catch(error => { + dispatch(saveSectorMappingError(error)); + }); + }; +} + +function processResponse(response) { + if (response.status === 500 || response.error) { + if (response.error) { + throw (response.error); + } else { + throw (response.statusText); + } + } +} + +export default saveSectorMappings; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js index 37481655b34..f640e5ffd3e 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/startupAction.js @@ -4,7 +4,6 @@ export const FETCH_SECTOR_MAP_ERROR = 'FETCH_SECTOR_MAP_ERROR'; export const FETCH_SECTORS_PENDING = 'FETCH_SECTORS_PENDING'; export const FETCH_SECTORS_SUCCESS = 'FETCH_SECTORS_SUCCESS'; export const FETCH_SECTORS_ERROR = 'FETCH_SECTORS_ERROR'; - export const FETCH_SCHEMES_PENDING = 'FETCH_SCHEMES_PENDING'; export const FETCH_SCHEMES_SUCCESS = 'FETCH_SCHEMES_SUCCESS'; export const FETCH_SCHEMES_ERROR = 'FETCH_SCHEMES_ERROR'; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx index 0d368a9a4f8..28bcbe0b110 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx @@ -4,7 +4,7 @@ import { bindActionCreators } from 'redux'; import PropTypes from 'prop-types'; import { SectorMappingContext } from './Startup'; import './css/style.css'; -//import * as Utils from '../utils/Utils'; +import * as Validation from '../utils/Validation'; import { DST_SCHEME_SECTOR, SRC_SCHEME_SECTOR, @@ -13,15 +13,21 @@ import { DST_SECTOR, TYPE_SRC, TYPE_DST, - ALL_SCHEMES + ALL_SCHEMES, SECTOR } from '../constants/Constants'; -import {sendNDDError, sendNDDPending, sendNDDSaving} from "../../ndd/reducers/saveNDDReducer"; +import { + sendSectorMappingError, + sendSectorMappingPending, + sendSectorMappingSaving +} from "../reducers/saveSectorMappingReducer"; import {updateActivitiesError, updateActivitiesPending} from "../../ndd/reducers/updateActivitiesReducer"; -import saveNDD from "../../ndd/actions/saveNDD"; +import saveSectorMappings from "../actions/saveSectorMappings"; import updateActivities from "../../ndd/actions/updateActivities"; import SectorsHeader from "./SectorsHeader"; import Notifications from "./Notifications"; +import HeaderActions from "./HeaderActions"; +import SectorMappingTable from "./SectorMappingTable"; class FormSectors extends Component { @@ -36,15 +42,13 @@ class FormSectors extends Component { updatedActivities: false, blockUI: false, unsavedChanges: false, - saved: false, - level: 0 + saved: false }; this.addRow = this.addRow.bind(this); this.saveAll = this.saveAll.bind(this); this.onRowChange = this.onRowChange.bind(this); this.remove = this.remove.bind(this); this.clearMessages = this.clearMessages.bind(this); - // this.onChangeMainProgram = this.onChangeMainProgram.bind(this); this.onChangeMainScheme = this.onChangeMainScheme.bind(this); this.clearAll = this.clearAll.bind(this); } @@ -55,7 +59,6 @@ class FormSectors extends Component { // Load Source Scheme Sector selected this.setState(() => { - console.log('mappings: ', mappings); if (mappings[SRC_SCHEME_SECTOR]) { const src = {id: mappings[SRC_SCHEME_SECTOR].id, value: mappings[SRC_SCHEME_SECTOR].value}; return {src}; @@ -115,7 +118,6 @@ class FormSectors extends Component { componentDidUpdate(prevProps) { if (prevProps !== this.props) { - // eslint-disable-next-line react/no-did-update-set-state this.setState(previousState => { if (!previousState.saved) { return { saved: true }; @@ -126,25 +128,49 @@ class FormSectors extends Component { } } - //TODO: check addRow() addRow() { this.clearMessages(); this.setState(previousState => { const data = [...previousState.data]; const pair = { + id: `${Math.random() * -1}`, [SRC_SECTOR]: {}, - [DST_SECTOR]: {}, - id: Math.random() * -1 + [DST_SECTOR]: {} }; data.push(pair); setTimeout(() => (window.scrollTo(0, document.body.scrollHeight)), 500); return { data, unsavedChanges: true, adding: true }; }); + } + + onRowChange(sector, type, id) { + const { data } = this.state; + this.clearMessages(); + // Find row. + const row = data.find(i => i.id === id); + // Remove row. + data.splice(data.findIndex(i => i.id === id), 1); + // Set row with new values. + row[type + SECTOR] = (sector && sector.length === 0) ? {} : sector[0]; + data.push(row); + + this.setState(data); + this.setState({ unsavedChanges: true }); } - onRowChange() {} - remove(row) {} + remove(row) { + const { translations, trnPrefix } = this.context; + if (window.confirm(translations[`${trnPrefix}confirm-remove-row`])) { + this.clearMessages(); + this.setState(previousState => { + const data = [...previousState.data]; + data.splice(data.findIndex(i => i.id === row.id), 1); + return { data }; + }); + this.setState({ unsavedChanges: true }); + } + } clearMessages() { this.setState({ @@ -152,7 +178,36 @@ class FormSectors extends Component { }); } - saveAll() {} + saveAll() { + const { data, src, dst } = this.state; + const { _saveSectorMappings, translations } = this.props; + const { api, trnPrefix } = this.context; + + const checkSchemes = Validation.checkSchemes(src, dst); + if (checkSchemes === 0) { + const checkMaps = Validation.checkMappings(data); + if (checkMaps === 0) { + const mappsToSave = []; + data.forEach(item => { + const mapping = {}; + mapping[SRC_SECTOR] = item[SRC_SECTOR].id; + mapping[DST_SECTOR] = item[DST_SECTOR].id; + mappsToSave.push(mapping); + }); + _saveSectorMappings(mappsToSave, api.baseEndpoint); + this.setState({ unsavedChanges: false }); + this.clearMessages(); + } else { + this.setState({ + validationErrors: translations[`${trnPrefix}sector_validation_error_${checkMaps}`], + }); + } + } else { + this.setState({ + validationErrors: translations[`${trnPrefix}scheme_validation_error_${checkSchemes}`], + }); + } + } onChangeMainScheme(type, scheme) { const { translations, trnPrefix } = this.context; @@ -181,10 +236,9 @@ class FormSectors extends Component { } else { // Revert to previous Sector. this.setState(previousState => previousState); - // TODO: set focus in the selector. } } else { - // Nothing -> Program. + // Nothing -> Scheme. this.setState(previousState => ({ [type]: newScheme })); this[`${type}_`] = newScheme; autoAddRow = true; @@ -208,7 +262,7 @@ class FormSectors extends Component { } render() { - const { data, validationErrors, src, dst, updatedActivities, unsavedChanges, saved, level } = this.state; + const { data, validationErrors, src, dst, updatedActivities, unsavedChanges, saved } = this.state; const { error, pending, translations, updating, errorUpdating, saving } = this.props; const { trnPrefix } = this.context; @@ -223,7 +277,7 @@ class FormSectors extends Component { messages.push({ isError: false, text: translations[`${trnPrefix}notification-saved-ok`] }); } - //TODO: check this flags commented + //TODO: check this flags // if (updatedActivities) { // messages.push({ // isError: false, @@ -250,10 +304,28 @@ class FormSectors extends Component { dst={dst} key={Math.random()} busy={updating} - onChange={this.onChangeMainScheme} - /> + onChange={this.onChangeMainScheme} /> + + 0} /> + +
); } @@ -268,7 +340,7 @@ FormSectors.propTypes = { pending: PropTypes.bool, updating: PropTypes.bool, errorUpdating: PropTypes.string, - _saveSectorMapping: PropTypes.func.isRequired, // _saveNDD + _saveSectorMappings: PropTypes.func.isRequired, //_updateActivities: PropTypes.func.isRequired, saving: PropTypes.bool.isRequired }; @@ -282,14 +354,14 @@ FormSectors.defaultProps = { const mapStateToProps = state => ({ translations: state.translationsReducer.translations, - // error: sendNDDError(state.saveNDDReducer), - // saving: sendNDDSaving(state.saveNDDReducer), - // pending: sendNDDPending(state.saveNDDReducer), + error: sendSectorMappingError(state.saveSectorMappingReducer), + saving: sendSectorMappingSaving(state.saveSectorMappingReducer), + pending: sendSectorMappingPending(state.saveSectorMappingReducer), // updating: updateActivitiesPending(state.updateActivitiesReducer), // errorUpdating: updateActivitiesError(state.updateActivitiesReducer) }); const mapDispatchToProps = dispatch => bindActionCreators({ - // _saveNDD: saveNDD, + _saveSectorMappings: saveSectorMappings, // _updateActivities: updateActivities }, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(FormSectors); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx index 8ed0a5d5d27..d635fdd4b1c 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Header.jsx @@ -11,7 +11,7 @@ class Header extends Component { translations, onAddRow, onSaveAll, onRevertAll, src, dst, busy, dataPresent, unsavedChanges } = this.props; //onUpdateActivities - const { trnPrefix, isIndirect } = this.context; + const { trnPrefix } = this.context; return (
@@ -47,15 +47,6 @@ class Header extends Component { disabled={busy || !unsavedChanges}> {translations[`${trnPrefix}button-revert-all-edits`]} - {/*{isIndirect ? (*/} - {/* */} - {/* {translations[`${trnPrefix}button-update-activities`]}*/} - {/* */} - {/*) : null}*/}
diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx new file mode 100644 index 00000000000..b77253d8ddd --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx @@ -0,0 +1,79 @@ +import React, {Component} from "react"; +import {SectorMappingContext} from "./Startup"; +import {connect} from "react-redux"; +import {bindActionCreators} from "redux"; +import PropTypes from "prop-types"; +import './css/style.css'; + +class HeaderActions extends Component { + render() { + const { + translations, onAddRow, onSaveAll, onRevertAll, src, dst, onUpdateActivities, busy, dataPresent, unsavedChanges + } = this.props; + const {trnPrefix} = this.context; + + return ( +
+
+
+ {(src && dst) ? ( + <> + + + {translations[`${trnPrefix}add-new`]} {' '} + + + {translations[`${trnPrefix}insert-data`]} + + + ) : null} + / + {`* ${translations[`${trnPrefix}required-fields`]}`} + + + + + +
+
+
+ ); + } +} + +HeaderActions.contextType = SectorMappingContext; + +HeaderActions.propTypes = { + onAddRow: PropTypes.func.isRequired, + onSaveAll: PropTypes.func.isRequired, + onRevertAll: PropTypes.func.isRequired, + translations: PropTypes.object.isRequired, + //onUpdateActivities: PropTypes.func.isRequired, + src: PropTypes.object, + dst: PropTypes.object, + busy: PropTypes.bool.isRequired, + dataPresent: PropTypes.bool, + unsavedChanges: PropTypes.bool.isRequired +}; + +HeaderActions.defaultProps = { + src: undefined, + dst: undefined, + dataPresent: false +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); + +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(HeaderActions); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx index 5d06bbdaa6b..c262308e97d 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx @@ -18,7 +18,6 @@ import fetchSettings from "../actions/fetchSettings"; import BlockUI from "./common/BlockUI"; import FormSectors from "./FormSectors"; - class Main extends Component { constructor(props) { super(props); @@ -27,17 +26,14 @@ class Main extends Component { } componentDidMount() { - const { - _fetchSectorMappings, _fetchSchemes, api, _fetchLayout, _fetchSettings - } = this.props; + const { _fetchSectorMappings, _fetchSchemes, api, _fetchLayout, _fetchSettings } = this.props; _fetchSettings().then(settings => { _fetchLayout().then(layout => { if (layout && layout.logged && layout.administratorMode === true) { _fetchSectorMappings(api.mappingConfig); _fetchSchemes(api.allSchemes); - - this.setState({ isSuperAdmin: layout.email.indexOf('super') === 0, settings }); + //this.setState({ isSuperAdmin: layout.email.indexOf('super') === 0, settings }); } else { window.location.replace('/login.do'); } @@ -45,15 +41,12 @@ class Main extends Component { }).catch(e => console.error(e)); } - // pendingNDD -> pendingSectorMapping - // pendingPrograms -> pendingSectors shouldComponentRender() { const { pendingSectorMapping, pendingSectors } = this.props; return !pendingSectorMapping && !pendingSectors; } render() { - // ndd -> mappings const { mappings, schemes, api, trnPrefix } = this.props; const { translations } = this.context; const { isSuperAdmin, settings } = this.state; @@ -84,8 +77,8 @@ class Main extends Component { Main.contextType = SectorMappingContext; Main.propTypes = { - _fetchSectorMappings: PropTypes.func.isRequired, // _fetchNDD - _fetchSchemes: PropTypes.func.isRequired, // _fetchPrograms + _fetchSectorMappings: PropTypes.func.isRequired, + _fetchSchemes: PropTypes.func.isRequired, _fetchLayout: PropTypes.func.isRequired, _fetchSettings: PropTypes.func.isRequired, api: PropTypes.object.isRequired diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx index 0f9bcf44641..117143479db 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingAdminNavigator.jsx @@ -2,22 +2,12 @@ import React, { useState } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import PropTypes from 'prop-types'; -//import AdminNDDIndirectProgramApp from '../indirect'; import AdminSectorMappingApp from '../sector'; -// eslint-disable-next-line no-unused-vars import styles from './css/style.css'; const SectorMappingAdminNavigator = ({ translations }) => { - // const [key, setKey] = useState('indirect'); - //const trnPrefix = 'amp.admin.sectorMapping:'; - return ( - // - <> - - - - ); + return ( ); }; const mapStateToProps = state => ({ diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx new file mode 100644 index 00000000000..7770a0d8957 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx @@ -0,0 +1,76 @@ +import React, {Component, useState} from "react"; +import {SectorMappingContext} from "./Startup"; +import PropTypes from "prop-types"; +import {bindActionCreators} from "redux"; +import {connect} from "react-redux"; +import './css/style.css'; +import HelpTooltip from "./common/HelpTooltip"; +import RequiredMark from "../../ndd/components/common/RequiredMark"; +import SectorMappingTableRow from "./SectorMappingTableRow"; + +class SectorMappingTable extends Component { + + render() { + const { list, translations, onChange, remove, dst, src, busy } = this.props; + const { trnPrefix } = this.context; + + return ( +
+ + + + + + + + + + {list.map(row => ( + + ))} + +
+ + {translations[`${trnPrefix}src-sectors`]} + + + + {translations[`${trnPrefix}dst-sectors`]} + + {translations[`${trnPrefix}actions`]}
+
+ ); + } +} + +SectorMappingTable.contextType = SectorMappingContext; + +SectorMappingTable.propTypes = { + list: PropTypes.array.isRequired, + onChange: PropTypes.func.isRequired, + remove: PropTypes.func.isRequired, + src: PropTypes.object, + dst: PropTypes.object, + translations: PropTypes.object.isRequired, + busy: PropTypes.bool.isRequired +}; + +SectorMappingTable.defaultProps = { + src: undefined, + dst: undefined +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); + +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(SectorMappingTable); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTableRow.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTableRow.jsx new file mode 100644 index 00000000000..6a1eec394f5 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTableRow.jsx @@ -0,0 +1,123 @@ +import React, {Component} from "react"; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import PropTypes from 'prop-types'; +import {SectorMappingContext} from "./Startup"; +import Select from "./Select"; +import {ALL_SCHEMES, SECTOR, TYPE_DST, TYPE_SRC} from "../constants/Constants"; + +class SectorMappingTableRow extends Component { + + constructor(props) { + super(props); + this.state = { + id: '', + [TYPE_SRC + SECTOR]: undefined, + [TYPE_DST + SECTOR]: undefined + }; + this.onSelectChange = this.onSelectChange.bind(this); + this.getOptionsByType = this.getOptionsByType.bind(this); + this.getSelectedOptionByType = this.getSelectedOptionByType.bind(this); + } + + componentDidMount() { + const {rowData} = this.props; + if (rowData) { + this.setState(rowData); + } + } + + onSelectChange(selection, rowData, type) { + const { onChange } = this.props; + + if (rowData) { + const newState = { + ...rowData, + [type + SECTOR]: selection.length > 0 ? selection[0] : {} + } + this.setState(newState); + onChange(selection, type, rowData.id); + } + } + + getOptionsByType(type, mappings, scheme) { + const allSchemes = mappings[ALL_SCHEMES]; + const schemeWithChildren = allSchemes.find(s => s.id === scheme.id); + return schemeWithChildren.children; + } + + getSelectedOptionByType(type, rowData) { + const selected = []; + const propName = `${type}${SECTOR}`; + + if (rowData[propName] && rowData[propName].id !== undefined) { + selected.push(rowData[propName]); + } + return selected; + } + + render() { + const { rowData, remove, src, dst, disabled } = this.props; + const { mappings, translations, trnPrefix } = this.context; + + return ( + + +
+ this.onSelectChange(selection, rowData, TYPE_DST)} + rowData={rowData} + type={TYPE_DST} + disabled={disabled} /> +
+ + + {!disabled ? ( + remove(rowData)} /> + ) : null} + + + ); + } +} + +SectorMappingTableRow.contextType = SectorMappingContext; + +SectorMappingTableRow.propTypes = { + rowData: PropTypes.object.isRequired, + onChange: PropTypes.func.isRequired, + remove: PropTypes.func.isRequired, + src: PropTypes.object, + dst: PropTypes.object, + disabled: PropTypes.bool.isRequired +}; + +SectorMappingTableRow.defaultProps = { + src: undefined, + dst: undefined +}; + +const mapStateToProps = state => ({ + translations: state.translationsReducer.translations +}); +const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); +export default connect(mapStateToProps, mapDispatchToProps)(SectorMappingTableRow); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx index 5514d0405e0..abf2110b523 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx @@ -23,23 +23,21 @@ class SectorsHeader extends Component { p.visible === false)} selected={dst ? [dst] : []} - onChange={onChange.bind(null, TYPE_DST)} - level={0} /> + onChange={onChange.bind(null, TYPE_DST)} /> @@ -59,14 +57,12 @@ SectorsHeader.propTypes = { src: PropTypes.object, dst: PropTypes.object, onChange: PropTypes.func.isRequired, - busy: PropTypes.bool.isRequired, - level: PropTypes.number + busy: PropTypes.bool.isRequired }; SectorsHeader.defaultProps = { src: undefined, - dst: undefined, - level: 1 + dst: undefined }; const mapStateToProps = state => ({ diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx index d4874c61cc9..5f66edb87b5 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Select.jsx @@ -14,8 +14,8 @@ class Select extends Component { this.drawSelector = this.drawSelector.bind(this); } onChangeSelect(selected) { - const { onChange, level } = this.props; - onChange(selected, level); + const { onChange, rowData, type } = this.props; + onChange(selected, rowData, type); } drawSelector() { @@ -59,7 +59,6 @@ Select.propTypes = { placeholder: PropTypes.string.isRequired, selected: PropTypes.array, onChange: PropTypes.func.isRequired, - level: PropTypes.number.isRequired, disabled: PropTypes.bool.isRequired }; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx index 130548de02f..131ac5ba65d 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx @@ -45,6 +45,6 @@ Startup.propTypes = { translations: PropTypes.object.isRequired, children: PropTypes.object.isRequired, _fetchTranslations: PropTypes.func.isRequired, - api: PropTypes.object.isRequired, + api: PropTypes.object, defaultTrnPack: PropTypes.object.isRequired, }; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx index 2251af8a961..e70f9a35878 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/common/HelpTooltip.jsx @@ -4,8 +4,6 @@ import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import {SectorMappingContext} from '../Startup'; - -// eslint-disable-next-line no-unused-vars import styles from '../css/style.css'; class HelpTooltip extends Component { @@ -18,7 +16,6 @@ class HelpTooltip extends Component { ); return ( - {/* eslint-disable-next-line jsx-a11y/alt-text */} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json index bda7b051f92..94e9c33f56c 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/config/initialTranslations.json @@ -1,6 +1,6 @@ { "amp.admin.sectorMapping:page-title": "Aid Management Platform - Sector Mapping Manager", - "amp.admin.sectorMapping:menu-item-sectors-mapping": "Sectors Mapping", + "amp.admin.sectorMapping:menu-item-sector-mappings": "Sector Mappings", "amp.admin.sectorMapping:mappings-manager": "Sector Mapping Manager", "amp.admin.sectorMapping:title": "Sectors Mapping", "amp.admin.sectorMapping:sub-title": "This module manages the mappings between primary and secondary sectors.", diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx index f6c002cdcea..b312f057059 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx @@ -1,19 +1,13 @@ -//export const SRC_PROGRAM = 'src-program'; export const SRC_SCHEME_SECTOR = 'src-scheme-sector'; export const SRC_SECTOR = 'src-sector'; -//export const DST_PROGRAM = 'dst-program'; export const DST_SCHEME_SECTOR = 'dst-scheme-sector'; export const DST_SECTOR = 'dst-sector'; -//export const ALL_PROGRAMS = 'all-programs'; export const ALL_SCHEMES = 'all-schemes'; export const ALL_SECTORS = 'all-sectors'; -//export const PROGRAM_MAPPING = 'program-mapping'; export const SECTOR_MAPPING = 'sector-mapping'; -//export const PROGRAM = '-program'; -export const SECTOR = '-program'; +export const SECTOR = '-sector'; export const VALUE = 'value'; export const CHILDREN = 'children'; -export const STATE_LEVEL_FIELD = 'srcLvl'; export const TYPE_SRC = 'src'; export const TYPE_DST = 'dst'; export const LAYOUT_EP = '/rest/security/layout'; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js index 5f79d3ca3a4..351d85054d0 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/index.js @@ -7,11 +7,9 @@ import rootReducer from './reducers/rootReducer'; import defaultTrnPack from './config/initialTranslations.json'; import Startup from './components/Startup'; import SectorMappingAdminNavigator from './components/SectorMappingAdminNavigator'; -// eslint-disable-next-line no-unused-vars import style from './components/css/style.css'; const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; - class AdminSectorMappingApp extends Component { constructor(props) { super(props); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js index efc7c2f7dc7..e17af136f3e 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/saveSectorMappingReducer.js @@ -2,7 +2,6 @@ import { SAVE_SECTOR_MAP_SUCCESS, SAVE_SECTOR_MAP_PENDING, SAVE_SECTOR_MAP_ERROR const initialState = { saving: false, - //NDDs: [], SectorMappings: [], error: null }; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js index afec98f3057..9b26d665c84 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/startupReducer.js @@ -2,20 +2,17 @@ import { FETCH_SECTOR_MAP_SUCCESS, FETCH_SECTOR_MAP_PENDING, FETCH_SECTOR_MAP_ERROR, - FETCH_SECTORS_SUCCESS, - FETCH_SECTORS_PENDING, - FETCH_SECTORS_ERROR, FETCH_SCHEMES_PENDING, FETCH_SCHEMES_SUCCESS, FETCH_SCHEMES_ERROR } from '../actions/startupAction'; const initialState = { - pendingSectorMapping: false, // pendingNDD - pendingSchemes: false, // pendingPrograms - sectorMappings: [], //NDDs + pendingSectorMapping: false, + pendingSchemes: false, + sectorMappings: [], error: null, - schemes: [] //programs + schemes: [] }; export default function startupReducer(state = initialState, action) { diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json index 864c03c3752..f5b9e5b9403 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json @@ -4,8 +4,31 @@ "amp.admin.sectorMapping:tooltip-indirect-sector": "The indirect sector is mapped against your reference sector. If your reference sector is your national development strategy, the indirect sector could be any regional/international strategy (ex. SDGs, New Deal, etc.) or vice-versa. Please note that the indirect sector will be displayed in the inner ring of the dashboard. The assigned percentage will be automatically distributed. This sector will not be seen in the activity form.", "amp.admin.sectorMapping:choose_main_src_sector": "Choose a Source Sector", "amp.admin.sectorMapping:choose_main_dst_sector": "Choose a Destination Sector", - "amp.admin.sectorMapping:src-sector-lvl-1": "Source Sector", - "amp.admin.sectorMapping:dst-sector-lvl-1": "Destination Sector", + "amp.admin.sectorMapping:src-scheme-sector": "Source Scheme Sector", + "amp.admin.sectorMapping:dst-scheme-sector": "Destination Scheme Sector", + "amp.admin.sectorMapping:src-sector": "Source Sector", + "amp.admin.sectorMapping:dst-sector": "Destination Sector", + "amp.admin.sectorMapping:src-sectors": "Source Sectors", + "amp.admin.sectorMapping:dst-sectors": "Destination Sectors", "amp.admin.sectorMapping:warning_on_change_main_sector": "If you change the Source Sector or the Destination Sector the existing mappings will be deleted, are you sure you want to continue?", - "amp.admin.sectorMapping:notification-saved-ok": "All mappings saved correctly." + "amp.admin.sectorMapping:notification-saved-ok": "All mappings saved correctly.", + "amp.admin.sectorMapping:add-new": "Add New", + "amp.admin.sectorMapping:insert-data": "(insert data to the new field)", + "amp.admin.sectorMapping:button-save-all-edits": "Save all edits", + "amp.admin.sectorMapping:button-revert-all-edits": "Revert all changes", + "amp.admin.sectorMapping:button-update-activities": "Update All Activities", + "amp.admin.sectorMapping:button-update-activities-confirmation": "Are you sure you want to update all activities with the current mappings? (This process could take several minutes)", + "amp.admin.sectorMapping:tooltip-src-sector-list": "Sectors belonging to the Source Scheme Sector", + "amp.admin.sectorMapping:tooltip-dst-sector-list": "Sectors belonging to the Destination Scheme Sector", + "amp.admin.sectorMapping:confirm-remove-row": "Are you sure you want to remove this mapping? (remember to click Save All button to confirm your changes).", + "amp.admin.sectorMapping:scheme_validation_error_1": "Error: Source Scheme Sector not selected.", + "amp.admin.sectorMapping:scheme_validation_error_2": "Error: Destination Scheme Sector not selected.", + "amp.admin.sectorMapping:scheme_validation_error_3": "Error: Source Scheme Sector and Destination Scheme Sector must be different.", + "amp.admin.sectorMapping:sector_validation_error_1": "Error: Source Sector not selected.", + "amp.admin.sectorMapping:sector_validation_error_2": "Error: Destination Sector not selected.", + "amp.admin.sectorMapping:sector_validation_error_3": "Error: Duplicate values found in mapping.", + "amp.admin.sectorMapping:choose_src_scheme": "Choose a Source Scheme", + "amp.admin.sectorMapping:choose_dst_scheme": "Choose a Destination Scheme", + "amp.admin.sectorMapping:choose_src_sector": "Choose a Source Sector", + "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector" } diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js index 8f0440eca78..ad851dd2002 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/index.js @@ -1,11 +1,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { - applyMiddleware, bindActionCreators, compose, createStore -} from 'redux'; +import { applyMiddleware, bindActionCreators, compose, createStore } from 'redux'; import thunk from 'redux-thunk'; import { connect, Provider } from 'react-redux'; - import Main from '../components/Main'; import rootReducer from '../reducers/rootReducer'; import defaultTrnPack from './config/initialTranslations.json'; @@ -14,17 +11,12 @@ import Startup, {SectorMappingContext} from '../components/Startup'; const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const API = { - // mappingConfig: '/rest/ndd/indirect-programs-mapping-config', - // mappingSave: '/rest/ndd/indirect-programs-mapping', - // programs: '/rest/ndd/available-indirect-programs', - // programsSave: '/rest/ndd/update-source-destination-indirect-programs', - - mappingConfig: '/rest/sectors-mapping/sectors-mapping-config', - allMappings: '/rest/sectors-mapping/all-mappings', - allSchemes: '/rest/sectors-mapping/all-schemes', - sectorsClassified: '/rest/sectors-mapping/sectors-classified/', - sectorsByScheme: '/rest/sectors-mapping/sectors-by-scheme/', - baseEndpoint: '/rest/sectors-mapping' // Create (POST) and Delete (DELETE) endpoints + mappingConfig: '/rest/sector-mappings/sector-mappings-config', + allMappings: '/rest/sector-mappings/all-mappings', + allSchemes: '/rest/sector-mappings/all-schemes', + sectorsClassified: '/rest/sector-mappings/sectors-classified/', // TODO: check if this endpoint is still used + sectorsByScheme: '/rest/sector-mappings/sectors-by-scheme/', + baseEndpoint: '/rest/sector-mappings' // Create (POST) and Delete (DELETE) endpoints }; const TRN_PREFIX = 'amp.admin.sectorMapping:'; @@ -36,21 +28,6 @@ class AdminSectorMappingApp extends Component { } render() { - // const { translations } = this.context; - // const { selected } = this.props; - // return ( - // selected - // ? ( - // - // - //

Admin Sector Mapping (Index 2°)

- //

{translations[`${TRN_PREFIX}title`]}

- //
{translations[`${TRN_PREFIX}sub-title`]}
- // {/*
*/} - // - // - // ) : null); - const { translations } = this.context; return ( @@ -67,7 +44,6 @@ class AdminSectorMappingApp extends Component { AdminSectorMappingApp.contextType = SectorMappingContext; AdminSectorMappingApp.propTypes = { - selected: PropTypes.bool.isRequired }; const mapStateToProps = state => ({ diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js new file mode 100644 index 00000000000..7046f6e8956 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js @@ -0,0 +1,39 @@ +import {DST_SECTOR, SRC_SECTOR} from "../constants/Constants"; + +export function showAlert(message) { + alert(message); +} + +export function checkSchemes(src, dst) { + if (!src) return 1; + else if (!dst) return 2; + else if (src.id === dst.id) return 3; + else return 0; +} + +export function checkMappings(data) { + console.log(data); + let errorNumber = 0; + if (data && data.length > 0) { + data.every((item) => { + if (Object.keys(item[SRC_SECTOR]).length === 0) { + errorNumber = 1; + return false; + } else if (Object.keys(item[DST_SECTOR]).length === 0) { + errorNumber = 2; + return false; + } else { + const arrayFound = data.filter(toFind => ( + toFind[SRC_SECTOR].id === item[SRC_SECTOR].id && toFind[DST_SECTOR].id === item[DST_SECTOR].id + )); + if (arrayFound.length > 1) { + errorNumber = 3; + return false; + } + } + return true; + }); + } + + return errorNumber; +} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java index 3b9cb0b9ec6..0aa6ef5e3f7 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingEndpoints.java @@ -3,8 +3,6 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import org.digijava.kernel.ampapi.endpoints.gpi.ValidationUtils; -import org.digijava.kernel.ampapi.endpoints.ndd.IndirectProgramMappingConfiguration; import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.GenericSelectObjDTO; import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.MappingConfigurationDTO; import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.SchemaClassificationDTO; @@ -12,24 +10,21 @@ import org.digijava.kernel.ampapi.endpoints.util.ApiMethod; import org.digijava.kernel.exception.DgException; import org.digijava.module.aim.dbentity.AmpSectorMapping; - import javax.ws.rs.*; import javax.ws.rs.core.MediaType; -import java.util.Arrays; import java.util.Collection; import java.util.List; /** * @author Diego Rossi */ -@Path("sectors-mapping") -@Api("sectors-mapping") +@Path("sector-mappings") +@Api("sector-mappings") public class SectorMappingEndpoints { - private final SectorMappingService smService = new SectorMappingService(); @GET - @Path("sectors-mapping-config") + @Path("sector-mappings-config") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") @ApiMethod(id = "getMappingsConf") @ApiOperation("Returns configuration saved for sectors mapping.") @@ -49,7 +44,7 @@ public Collection getAllSectorMappings() { @GET @Path("all-schemes") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") - @ApiMethod(id = "getClassifiedSectors") + @ApiMethod(id = "getAllSchemes") @ApiOperation("Returns all schemes and his classifications.") public List getAllSchemes() { return smService.getAllSchemes(); @@ -58,7 +53,7 @@ public List getAllSchemes() { @GET @Path("sectors-by-scheme/{schemeId}") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") - @ApiMethod(id = "getClassifiedSectors") + @ApiMethod(id = "getSectorsByScheme") @ApiOperation("Returns a list of sectors level 1 by scheme id.") public List getSectorsByScheme(@ApiParam("Property value") @PathParam("schemeId") Long schemeId) { return smService.getSectorsByScheme(schemeId); @@ -66,16 +61,16 @@ public List getSectorsByScheme(@ApiParam("Property value") @POST @Path("") - @ApiMethod(id = "createSectorMapping") //TODO: add -> authTypes = AuthRule.IN_ADMIN, - @ApiOperation("Create a sector mapping.") - public void createSectorMapping(AmpSectorMapping mapping) throws DgException { - smService.createSectorsMapping(mapping); + @ApiMethod(authTypes = AuthRule.IN_ADMIN, id = "createSectorMapping") + @ApiOperation("Create a list of sector mapping.") + public void createSectorMapping(List mappings) throws DgException { + smService.createSectorMappings(mappings); } @DELETE @Path("/{idMapping}") @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") - @ApiMethod(id = "getClassifiedSectors") //TODO: add -> authTypes = AuthRule.IN_ADMIN, + @ApiMethod(authTypes = AuthRule.IN_ADMIN, id = "deleteSectorMapping") @ApiOperation("Delete a sector mapping.") public void deleteSectorMapping(@ApiParam("Property value") @PathParam("idMapping") Long idMapping) throws DgException { smService.deleteSectorMapping(idMapping); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index b151de415ba..dda100afa58 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -8,7 +8,6 @@ import org.digijava.kernel.exception.DgException; import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.util.SectorUtil; - import java.util.*; import java.util.stream.Collectors; @@ -18,14 +17,7 @@ public class SectorMappingService { private static final Logger LOGGER = Logger.getLogger(ValueConverter.class); - private Map sectorClasses = new HashMap<>(); - private String getSectorClassById(Long id) { - if (sectorClasses.isEmpty()) { - sectorClasses.put(1L, AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME); - sectorClasses.put(2L, AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME); - } - return sectorClasses.get(id); - } + public List getSectorsByScheme(final Long schemeId) { List sectors = new ArrayList<>(); @@ -45,8 +37,8 @@ public List getAllSchemes() { return SectorUtil.getAllSectorSchemesAndClassification(); } - public void createSectorsMapping(AmpSectorMapping mapping) throws DgException { - SectorUtil.createSectorMapping(mapping); + public void createSectorMappings(List mappings) throws DgException { + SectorUtil.createSectorMappings(mappings); } public void deleteSectorMapping(Long id) throws DgException { @@ -84,7 +76,6 @@ public MappingConfigurationDTO getMappingsConf() { } //region Private methods - private static AmpClassificationConfiguration findByName(List list, String name) { for (AmpClassificationConfiguration item : list) { if (item.getName().equals(name)) { diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java index 9df729ef17d..4f6faeadce8 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/MappingConfigurationDTO.java @@ -1,7 +1,6 @@ package org.digijava.kernel.ampapi.endpoints.sectorMapping.dto; import com.fasterxml.jackson.annotation.JsonProperty; - import java.util.Collection; import java.util.List; diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index f97fb6b17c5..eb1fabfc020 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -1270,33 +1270,8 @@ public static Collection getAllSectorMappings() { return col; } - public static Collection getSectorMappingsByPrimary(Long idPrimarySector) { - String queryString = null; - Session session = null; - Collection col = null; - Query qry = null; - - try { - session = PersistenceManager.getSession(); - queryString = "select asm from " + AmpSectorMapping.class.getName() + " asm " + - "where asm.srcSector.ampSectorId=:idPrimarySector"; - qry = session.createQuery(queryString); - qry.setParameter("idPrimarySector", idPrimarySector); - col = qry.list(); - session.flush(); - } catch (Exception ex) { - logger.error("Unable to get sectors mappings from database by primary " + ex.getMessage()); - ex.printStackTrace(System.out); - } - return col; - } - /** * adds an AmpSectorMapping - * - * @param sectorMapping - * @throws DgException - * If exception occurred */ public static void createSectorMapping(AmpSectorMapping sectorMapping) throws DgException { Session session = null; @@ -1312,6 +1287,25 @@ public static void createSectorMapping(AmpSectorMapping sectorMapping) throws Dg } } + public static void createSectorMappings(List sectorMappings) throws DgException { + Session session = null; + Collection existingMappings = getAllSectorMappings(); + try { + session = PersistenceManager.getRequestDBSession(); + + for (AmpSectorMapping em : existingMappings) { + session.delete(em); + } + for (AmpSectorMapping asm : sectorMappings) { + session.save(asm); + } + session.flush(); + } catch (Exception ex) { + logger.error("Unable to save the list of sector mapping " + ex.getMessage()); + throw new DgException(ex); + } + } + public static void deleteSectorMapping(Long id) throws DgException { Session session = null; try { From 53e34359ff9625ecde4f089e453a42d2ad068739 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Wed, 6 Dec 2023 15:51:51 -0300 Subject: [PATCH 04/85] AMP-30574 - Sector Mappings Updating all activities --- .../sectorMapping/actions/updateActivities.js | 76 ++++++++++++++++++ .../actions/updateActivitiesAction.js | 28 +++++++ .../sectorMapping/components/FormSectors.jsx | 49 +++++++----- .../components/HeaderActions.jsx | 10 +-- .../admin/sectorMapping/components/Main.jsx | 7 +- .../sectorMapping/constants/Constants.jsx | 2 +- .../sectorMapping/reducers/rootReducer.js | 6 +- .../reducers/updateActivitiesReducer.js | 46 +++++++++++ .../sector/config/initialTranslations.json | 4 +- .../admin/sectorMapping/utils/Validation.js | 1 - .../amp/onepager/util/ActivityUtil.java | 15 ++-- .../onepager/util/IndirectSectorUpdater.java | 78 ++++++++++++++++++ .../activity/InterchangeEndpoints.java | 34 ++++++-- ...cActivityIndirectSectorUpdaterService.java | 79 +++++++++++++++++++ .../AmpActivityIndirectSector.hbm.xml | 26 ++++++ .../dbentity/AmpActivityIndirectSector.java | 71 +++++++++++++++++ .../aim/dbentity/AmpActivitySector.hbm.xml | 8 +- .../aim/dbentity/AmpActivitySector.java | 39 +++++++-- amp/WEB-INF/web.xml | 23 +++--- amp/repository/aim/module-config.xml | 1 + 20 files changed, 539 insertions(+), 64 deletions(-) create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js create mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js create mode 100644 amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java create mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java create mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml create mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js new file mode 100644 index 00000000000..a33f5157e8a --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js @@ -0,0 +1,76 @@ +import { + invokeUpdateActivities, + updateActivitiesPending, + updateActivitiesError, + updateActivitiesSuccess +} from './updateActivitiesAction'; +import { UPDATE_ACTIVITIES_EP } from '../constants/Constants'; + +let timer; +const GET = 'GET'; + +function scheduleTimer(url, dispatch) { + clearInterval(timer); + timer = setInterval(() => checkForUpdate(url, dispatch), 1000); +} + +function checkForUpdate(url, dispatch) { + fetch(url).then(layoutRes => { + if (layoutRes.status === 200) { + return layoutRes.headers.get('X-Async-Status'); + } else { + return layoutRes.json(); + } + }).then((result) => { + if (result === null) { + clearInterval(timer); + return dispatch(updateActivitiesSuccess()); + } else if (!result.error) { + if (result === 'RUNNING') { + // its running do nothing + } else { + dispatch(updateActivitiesError({ msg: `result ${result}` })); + clearInterval(timer); + } + } else { + dispatch(updateActivitiesError({ msg: 'result false' })); + clearInterval(timer); + } + return result; + }).catch(error => { + dispatch(updateActivitiesError(error)); + }); +} + +function updateActivities() { + return dispatch => { + dispatch(updateActivitiesPending()); + const requestOptions = { + method: GET, + headers: { Prefer: 'respond-async' } + }; + fetch(UPDATE_ACTIVITIES_EP, requestOptions).then(layoutRes => { + if (layoutRes.status === 200) { + return layoutRes.headers.get('location'); + } else { + return layoutRes.json(); + } + }).then((result) => { + if (!result.error) { + dispatch(invokeUpdateActivities()); + scheduleTimer(result, dispatch); + // TODO better process generic errors coming from the API + } else if (result.error['0212']) { + const msg = result.error['0212'][0].PROCESS_ALREADY_RUNNING[0]; + dispatch(updateActivitiesError({ msg })); + } else { + dispatch(updateActivitiesError({ msg: 'result false' })); + } + return result; + }).catch(error => { + dispatch(updateActivitiesError(error)); + }); + }; +} + +export default updateActivities; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js new file mode 100644 index 00000000000..71cd05bf391 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js @@ -0,0 +1,28 @@ +export const UPDATE_ACTIVITIES_PENDING = 'UPDATE_ACTIVITIES_PENDING'; +export const UPDATE_ACTIVITIES_SUCCESS = 'UPDATE_ACTIVITIES_SUCCESS'; +export const UPDATE_ACTIVITIES_ERROR = 'UPDATE_ACTIVITIES_ERROR'; +export const INVOKE_ACTIVITIES_SUCCESS = 'INVOKE_ACTIVITIES_SUCCESS'; + +export function updateActivitiesPending() { + return { + type: UPDATE_ACTIVITIES_PENDING + }; +} + +export function invokeUpdateActivities() { + return { + type: INVOKE_ACTIVITIES_SUCCESS + }; +} +export function updateActivitiesSuccess() { + return { + type: UPDATE_ACTIVITIES_SUCCESS + }; +} + +export function updateActivitiesError(error) { + return { + type: UPDATE_ACTIVITIES_ERROR, + error + }; +} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx index 28bcbe0b110..778b6ce1b63 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx @@ -21,9 +21,9 @@ import { sendSectorMappingPending, sendSectorMappingSaving } from "../reducers/saveSectorMappingReducer"; -import {updateActivitiesError, updateActivitiesPending} from "../../ndd/reducers/updateActivitiesReducer"; +import {updateActivitiesError, updateActivitiesPending} from "../reducers/updateActivitiesReducer"; import saveSectorMappings from "../actions/saveSectorMappings"; -import updateActivities from "../../ndd/actions/updateActivities"; +import updateActivities from "../actions/updateActivities"; import SectorsHeader from "./SectorsHeader"; import Notifications from "./Notifications"; import HeaderActions from "./HeaderActions"; @@ -178,6 +178,22 @@ class FormSectors extends Component { }); } + onUpdateActivities = () => { + const { _updateActivities, translations } = this.props; + const { trnPrefix } = this.context; + const { data } = this.state; + const validateMappings = Validation.checkMappings(data); + if (validateMappings === 0) { + if (window.confirm(translations[`${trnPrefix}button-update-activities-confirmation`])) { + this.clearMessages(); + this.setState({ blockUI: true }); + _updateActivities(); + } + } else { + this.setState({ validationErrors: translations[`${trnPrefix}validation_error_${validateMappings}`] }); + } + } + saveAll() { const { data, src, dst } = this.state; const { _saveSectorMappings, translations } = this.props; @@ -249,6 +265,7 @@ class FormSectors extends Component { if (this.src_ && this.dst_ && autoAddRow) { this.addRow(); } + this.setState({ unsavedChanges: true }); } clearAll() { @@ -276,20 +293,12 @@ class FormSectors extends Component { if (!saving && !error && !unsavedChanges && saved) { messages.push({ isError: false, text: translations[`${trnPrefix}notification-saved-ok`] }); } - - //TODO: check this flags - // if (updatedActivities) { - // messages.push({ - // isError: false, - // text: translations[`${trnPrefix}update-activities-successful`] - // }); - // } - // if (updating) { - // messages.push({ - // isError: false, - // text: translations[`${trnPrefix}update-activities-wait`] - // }); - // } + if (updatedActivities) { + messages.push({ isError: false, text: translations[`${trnPrefix}update-activities-successful`] }); + } + if (updating) { + messages.push({ isError: false, text: translations[`${trnPrefix}update-activities-wait`] }); + } if (errorUpdating) { messages.push({ isError: true, @@ -341,7 +350,7 @@ FormSectors.propTypes = { updating: PropTypes.bool, errorUpdating: PropTypes.string, _saveSectorMappings: PropTypes.func.isRequired, - //_updateActivities: PropTypes.func.isRequired, + _updateActivities: PropTypes.func.isRequired, saving: PropTypes.bool.isRequired }; @@ -357,11 +366,11 @@ const mapStateToProps = state => ({ error: sendSectorMappingError(state.saveSectorMappingReducer), saving: sendSectorMappingSaving(state.saveSectorMappingReducer), pending: sendSectorMappingPending(state.saveSectorMappingReducer), - // updating: updateActivitiesPending(state.updateActivitiesReducer), - // errorUpdating: updateActivitiesError(state.updateActivitiesReducer) + updating: updateActivitiesPending(state.updateActivitiesReducer), + errorUpdating: updateActivitiesError(state.updateActivitiesReducer) }); const mapDispatchToProps = dispatch => bindActionCreators({ _saveSectorMappings: saveSectorMappings, - // _updateActivities: updateActivities + _updateActivities: updateActivities }, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(FormSectors); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx index b77253d8ddd..600fd9efc3c 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx @@ -37,10 +37,10 @@ class HeaderActions extends Component { disabled={busy || !unsavedChanges}> {translations[`${trnPrefix}button-revert-all-edits`]} - + @@ -56,7 +56,7 @@ HeaderActions.propTypes = { onSaveAll: PropTypes.func.isRequired, onRevertAll: PropTypes.func.isRequired, translations: PropTypes.object.isRequired, - //onUpdateActivities: PropTypes.func.isRequired, + onUpdateActivities: PropTypes.func.isRequired, src: PropTypes.object, dst: PropTypes.object, busy: PropTypes.bool.isRequired, diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx index c262308e97d..34fd0bf362a 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx @@ -47,7 +47,7 @@ class Main extends Component { } render() { - const { mappings, schemes, api, trnPrefix } = this.props; + const { mappings, schemes, api, trnPrefix, indirectSectorUpdatePending } = this.props; const { translations } = this.context; const { isSuperAdmin, settings } = this.state; @@ -64,8 +64,7 @@ class Main extends Component { - {/**/} - + ); @@ -91,7 +90,7 @@ const mapStateToProps = state => ({ pendingMappings: getSectorMappingPending(state.startupReducer), pendingSchemes: getSchemesPending(state.startupReducer), translations: state.translationsReducer.translations, - //indirectProgramUpdatePending: state.updateActivitiesReducer.indirectProgramUpdatePending + indirectSectorUpdatePending: state.updateActivitiesReducer.indirectSectorUpdatePending }); const mapDispatchToProps = dispatch => bindActionCreators({ diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx index b312f057059..6ba1e9cd166 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx @@ -12,4 +12,4 @@ export const TYPE_SRC = 'src'; export const TYPE_DST = 'dst'; export const LAYOUT_EP = '/rest/security/layout'; export const SETTINGS_EP = '/rest/amp/settings'; -//export const UPDATE_ACTIVITIES_EP = '/rest/activity/updateMappings/async'; +export const UPDATE_ACTIVITIES_EP = '/rest/activity/updateSectorMappings/async'; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js index 5f869a2c3a6..8296ad3ea94 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js @@ -2,11 +2,11 @@ import { combineReducers } from 'redux'; import startupReducer from './startupReducer'; import saveSectorMappingReducer from './saveSectorMappingReducer'; import translationsReducer from '../../../../utils/reducers/translationsReducer'; -//import updateActivitiesReducer from './updateActivitiesReducer'; +import updateActivitiesReducer from './updateActivitiesReducer'; export default combineReducers({ startupReducer, translationsReducer, - saveSectorMappingReducer - //updateActivitiesReducer + saveSectorMappingReducer, + updateActivitiesReducer }); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js new file mode 100644 index 00000000000..db08c5a6053 --- /dev/null +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js @@ -0,0 +1,46 @@ +import { + UPDATE_ACTIVITIES_PENDING, UPDATE_ACTIVITIES_SUCCESS, UPDATE_ACTIVITIES_ERROR, INVOKE_ACTIVITIES_SUCCESS +} from '../actions/updateActivitiesAction'; + +const initialState = { + updating: false, + error: null, + indirectSectorUpdatePending: false +}; + +export default function updateActivitiesReducer(state = initialState, action) { + switch (action.type) { + case UPDATE_ACTIVITIES_PENDING: + return { + ...state, + updating: true, + error: null + }; + case INVOKE_ACTIVITIES_SUCCESS: + return { + ...state, + updating: false, + error: null, + indirectSectorUpdatePending: true + }; + case UPDATE_ACTIVITIES_SUCCESS: { + return { + ...state, + indirectSectorUpdatePending: false + }; + } + case UPDATE_ACTIVITIES_ERROR: + return { + ...state, + updating: false, + error: action.error.msg, + indirectSectorUpdatePending: false + }; + default: + return state; + } +} + +export const updateActivities = state => state; +export const updateActivitiesPending = state => state.updating; +export const updateActivitiesError = state => state.error; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json index f5b9e5b9403..0042642b3fe 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json @@ -30,5 +30,7 @@ "amp.admin.sectorMapping:choose_src_scheme": "Choose a Source Scheme", "amp.admin.sectorMapping:choose_dst_scheme": "Choose a Destination Scheme", "amp.admin.sectorMapping:choose_src_sector": "Choose a Source Sector", - "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector" + "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector", + "amp.admin.sectorMapping:update-activities-successful": "Activities where successfully updated", + "amp.admin.sectorMapping:update-activities-wait": "Updating activities, please wait..." } diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js index 7046f6e8956..d0b7250c6cf 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/utils/Validation.js @@ -12,7 +12,6 @@ export function checkSchemes(src, dst) { } export function checkMappings(data) { - console.log(data); let errorNumber = 0; if (data && data.length > 0) { data.every((item) => { diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java index bd5b999b3f9..41dc0f8a276 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java @@ -202,12 +202,12 @@ public static void prepareToSave(AmpActivityVersion a, AmpActivityVersion oldA, setActivityStatus(ampCurrentMember, draft, a, oldA, newActivity, context.isRejected()); } } - + public static AmpActivityVersion saveActivityNewVersion(AmpActivityVersion a, Collection translations, List cumulativeTranslations, AmpTeamMember ampCurrentMember, boolean draft, Session session, SaveContext context, EditorStore editorStore, Site site) throws Exception { - + boolean draftChange = detectDraftChange(a, draft); return saveActivityNewVersion(a, translations, cumulativeTranslations, ampCurrentMember, draft, draftChange, session, context, editorStore, site); @@ -335,6 +335,7 @@ public static AmpActivityVersion saveActivityNewVersion(AmpActivityVersion a, } updateIndirectPrograms(a, session); + updateIndirectSectors(a, session); logAudit(ampCurrentMember, a, newActivity); @@ -351,10 +352,14 @@ private static void updateIndirectPrograms(AmpActivityVersion a, Session session new IndirectProgramUpdater().updateIndirectPrograms(a, session); } + private static void updateIndirectSectors(AmpActivityVersion a, Session session) { + new IndirectSectorUpdater().updateIndirectSectors(a, session); + } + public static boolean detectDraftChange(AmpActivityVersion a, boolean draft) { return Boolean.TRUE.equals(a.getDraft()) != draft; } - + public static boolean isNewActivity(T a) { // it would be nicer to rely upon AMP ID, but some old activities may lack it return a.getAmpActivityId() == null; @@ -628,7 +633,7 @@ public static boolean canApprove(AmpTeamMember atm, Long activityTeamId, Approva } return false; } - + /** * An activity can be rejected only if: * 1. the activity is not new @@ -645,7 +650,7 @@ public static boolean canReject(AmpTeamMember atm, Boolean isDraft, Boolean isNe return BooleanUtils.isFalse(isNewActivity) && BooleanUtils.isFalse(isDraft) && isProjectValidationOn(getValidationSetting(atm)) && isApprover(atm); } - + /** * Detect if the teammember is approver of the workspace or is the teamlead of the ws * diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java new file mode 100644 index 00000000000..f66c1ec1ca4 --- /dev/null +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java @@ -0,0 +1,78 @@ +package org.dgfoundation.amp.onepager.util; + +import org.digijava.kernel.persistence.PersistenceManager; +import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.util.ActivityUtil; +import org.digijava.module.aim.util.activity.ActivityCloser; +import org.digijava.module.aim.util.activity.GenericUserHelper; +import org.hibernate.Session; + +import java.math.BigDecimal; +import java.util.*; + +import static java.util.Collections.emptySet; +import static java.util.stream.Collectors.*; + +/** + * @author Diego Rossi + */ +public class IndirectSectorUpdater { + + public AmpActivityVersion updateIndirectSectors(AmpActivityVersion activity, Session session) { + Map> mapping = loadMapping(session); + updateIndirectSectors(activity, mapping); + return activity; + } + + public void updateIndirectSectorMapping(Long ampActivityId) { + PersistenceManager.inTransaction(() -> { + try { + AmpActivityVersion o = ActivityUtil.loadActivity(ampActivityId); + ActivityCloser.cloneActivity(GenericUserHelper.getAmpTeamMemberModifier(o.getTeam()), + o, SaveContext.admin()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + @SuppressWarnings("unchecked") + private Map> loadMapping(Session session) { + List list = session + .createCriteria(AmpSectorMapping.class) + .setCacheable(true) + .list(); + + return list.stream().collect(groupingBy( + AmpSectorMapping::getSrcSector, + () -> new TreeMap<>(Comparator.comparing(AmpSector::getAmpSectorId)), + mapping(AmpSectorMapping::getDstSector, toCollection(this::newSetComparingById)))); + } + + private Set newSetComparingById() { + return new TreeSet<>(Comparator.comparing(AmpSector::getAmpSectorId)); + } + + private void updateIndirectSectors(AmpActivityVersion activity, Map> mapping) { + activity.getSectors().forEach((as) -> { + List indirectSectors = new ArrayList<>(mapping.getOrDefault(as.getSectorId(), emptySet())); + as.getIndirectSectors().clear(); + + if(!indirectSectors.isEmpty()) { + PercentagesUtil.SplitResult sr; + if (as.getSectorPercentage() != null) { + BigDecimal sum = PercentagesUtil.closestTo(as.getSectorPercentage(), + AmpActivityIndirectSector.PERCENTAGE_PRECISION); + sr = PercentagesUtil.split(sum, indirectSectors.size(), + AmpActivityIndirectSector.PERCENTAGE_PRECISION); + } else { sr = null; } + + for (int i = 0; i < indirectSectors.size(); i++) { + as.addIndirectSector(new AmpActivityIndirectSector(indirectSectors.get(i), + sr == null ? null : sr.getValueFor(i))); + } + } + }); + } + +} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java index 278891c9806..f7a04f85421 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java @@ -20,11 +20,7 @@ import org.digijava.kernel.ampapi.endpoints.activity.preview.PreviewWorkspace; import org.digijava.kernel.ampapi.endpoints.activity.utils.AmpMediaType; import org.digijava.kernel.ampapi.endpoints.activity.utils.ApiCompat; -import org.digijava.kernel.ampapi.endpoints.async.AsyncActivityIndirectProgramUpdaterService; -import org.digijava.kernel.ampapi.endpoints.async.AsyncApiService; -import org.digijava.kernel.ampapi.endpoints.async.AsyncResult; -import org.digijava.kernel.ampapi.endpoints.async.AsyncResultCacher; -import org.digijava.kernel.ampapi.endpoints.async.AsyncStatus; +import org.digijava.kernel.ampapi.endpoints.async.*; import org.digijava.kernel.ampapi.endpoints.common.JsonApiResponse; import org.digijava.kernel.ampapi.endpoints.errors.ApiError; import org.digijava.kernel.ampapi.endpoints.errors.ApiErrorResponseService; @@ -66,6 +62,7 @@ import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.X_ASYNC_RESULT_ID; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.X_ASYNC_STATUS; import static org.digijava.kernel.ampapi.endpoints.async.AsyncActivityIndirectProgramUpdaterService.PROGRAM_UPDATER_KEY; +import static org.digijava.kernel.ampapi.endpoints.async.AsyncActivityIndirectSectorUpdaterService.SECTOR_UPDATER_KEY; /** @@ -568,6 +565,33 @@ public Response getUpdateMappingsResult(@PathParam("result-id") String resultId) return buildResultId(PROGRAM_UPDATER_KEY + resultId); } + + @GET + @Path("/updateSectorMappings/async") + @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") + @ApiMethod(authTypes = AuthRule.IN_ADMIN, id = "updateSectorMappings", ui = false) + public Response updateSectorMappings() { + + String resultId = (String) TLSUtils.getRequest().getAttribute(X_ASYNC_RESULT_ID); + if (resultId == null) { + ApiErrorResponseService.reportError(BAD_REQUEST, ActivityErrors.ONLY_SYNC + .withDetails("Only sync process is allowed")); + } + if (!AsyncResultCacher.canAddAnotherUnique(SECTOR_UPDATER_KEY)) { + ApiErrorResponseService.reportError(BAD_REQUEST, ActivityErrors.PROCESS_ALREADY_RUNNING + .withDetails("Only one process at a time is allowed")); + } else { + // Start the process + AsyncActivityIndirectSectorUpdaterService.getInstance(). + updateIndirectSectors(SECTOR_UPDATER_KEY + resultId); + } + String location = String.format("%s/result/%s", UrlUtils.buildRequestUrl(TLSUtils.getRequest()), resultId); + return Response.ok() + .header("location", location) + .build(); + } + + private Response buildResultId(String resultId) { AsyncResult asyncResult = AsyncResultCacher.getAsyncResult(resultId); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java new file mode 100644 index 00000000000..f56916485af --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java @@ -0,0 +1,79 @@ +package org.digijava.kernel.ampapi.endpoints.async; + +import org.dgfoundation.amp.ar.viewfetcher.SQLUtils; +import org.dgfoundation.amp.onepager.util.IndirectSectorUpdater; +import org.digijava.kernel.ampapi.endpoints.common.JsonApiResponse; +import org.digijava.kernel.ampapi.endpoints.errors.ApiError; +import org.digijava.kernel.ampapi.endpoints.errors.ApiErrorMessage; +import org.digijava.kernel.ampapi.endpoints.errors.GenericErrors; +import org.digijava.kernel.persistence.PersistenceManager; +import org.digijava.kernel.request.TLSUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; + +public class AsyncActivityIndirectSectorUpdaterService { + public static final String SECTOR_UPDATER_KEY = "ASYNC_ACTIVITY_INDIRECT_SECTOR_UPDATER_KEY"; + private static AsyncActivityIndirectSectorUpdaterService instance; + public static AsyncActivityIndirectSectorUpdaterService getInstance() { + if (instance == null) { + instance = new AsyncActivityIndirectSectorUpdaterService(); + } + return instance; + } + + public void updateIndirectSectors(String key) { + + AsyncResultCacher.addAsyncResultsData(key, new AsyncResult(key)); + + ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); + executor.execute(() -> { + final List ampActivityIds = new ArrayList<>(); + PersistenceManager.inTransaction(() -> { + TLSUtils.populateMockTlsUtils(); + + PersistenceManager.getSession().doWork(conn -> { + + String query = " SELECT DISTINCT actSec.amp_activity_id " + + " FROM amp_sector_mapping secMap " + + " JOIN amp_activity_sector actSec ON secMap.src_sector_id = actSec.amp_sector_id " + + " WHERE EXISTS( " + + " SELECT 1 " + + " FROM amp_activity a, " + + " amp_activities_categoryvalues aacv, " + + " amp_category_class acc, " + + " amp_category_value acv, " + + " amp_global_settings gs " + + " WHERE a.amp_activity_id = aacv.amp_activity_id " + + " AND acv.amp_category_class_id = acc.id " + + " AND acc.keyname = 'activity_status' " + + " AND acv.id = aacv.amp_categoryvalue_id " + + " AND gs.settingsvalue::BIGINT = acv.id " + + " AND gs.settingsname = 'Closed activity status' " + + " AND actSec.amp_activity_id = a.amp_activity_id )"; + ampActivityIds.addAll(SQLUtils.fetchLongs(conn, query)); + }); + }); + final List errors = new ArrayList<>(); + Map details = new ConcurrentHashMap<>(); + + for (Long ampActivityId : ampActivityIds) { + try { + (new IndirectSectorUpdater()).updateIndirectSectorMapping(ampActivityId); + details.put(ampActivityId + "", "Activity Updated"); + } catch (Exception e) { + errors.add(GenericErrors.INTERNAL_ERROR.withDetails(ampActivityId + + "failed with error" + e.toString())); + } + } + AsyncResultCacher.getAsyncResult(key).getResults().add(new JsonApiResponse(ApiError.toError(errors), + details)); + AsyncResultCacher.getAsyncResult(key).setStatus(AsyncStatus.DONE); + }); + executor.shutdown(); + } +} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml new file mode 100644 index 00000000000..e7f82249f1e --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + AMP_ACTIVITY_INDIRECT_SECTOR_SEQ + + + + + + + + + + + + diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java new file mode 100644 index 00000000000..1ce91422658 --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java @@ -0,0 +1,71 @@ +package org.digijava.module.aim.dbentity; + +import org.digijava.module.aim.annotations.interchange.Interchangeable; +import org.digijava.module.aim.annotations.interchange.InterchangeableId; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author Diego Rossi + */ +public class AmpActivityIndirectSector implements Serializable, Cloneable { + + public static final int PERCENTAGE_PRECISION = 2; + + @InterchangeableId + @Interchangeable(fieldTitle = "Id") + private Long id; + + private AmpActivitySector activitySector; + + @Interchangeable(fieldTitle = "Sector", pickIdOnly = true) + private AmpSector sector; + + @Interchangeable(fieldTitle = "Percentage") + private BigDecimal percentage; + + public AmpActivityIndirectSector() { + } + + public AmpActivityIndirectSector(AmpSector sector, BigDecimal percentage) { + this.sector = sector; + this.percentage = percentage; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public AmpActivitySector getActivitySector() { + return activitySector; + } + + public void setActivitySector(AmpActivitySector activitySector) { + this.activitySector = activitySector; + } + + public BigDecimal getPercentage() { + return percentage; + } + + public void setPercentage(BigDecimal percentage) { + this.percentage = percentage; + } + + public AmpSector getSector() { + return sector; + } + + public void setSector(AmpSector sector) { + this.sector = sector; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml index 590f98e3524..04a5cf02964 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml @@ -12,7 +12,7 @@ - + @@ -22,6 +22,12 @@ + + + + + + diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java index 2c059ef98c3..108f77bb182 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java @@ -2,6 +2,8 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.digijava.kernel.ampapi.endpoints.activity.visibility.FMVisibility; import org.digijava.kernel.ampapi.endpoints.common.values.providers.SectorPossibleValuesProvider; @@ -28,14 +30,31 @@ public class AmpActivitySector implements Versionable, Serializable, Cloneable, @Interchangeable(fieldTitle = "Sector", importable = true, pickIdOnly = true, uniqueConstraint = true, interValidators = @InterchangeableValidator(RequiredValidator.class)) private AmpSector sectorId; - - @Interchangeable(fieldTitle="Sector Percentage", importable = true, percentageConstraint = true, + + @Interchangeable(fieldTitle="Sector Percentage", importable = true, percentageConstraint = true, fmPath = FMVisibility.PARENT_FM + "/sectorPercentage", interValidators = @InterchangeableValidator(RequiredValidator.class)) private Float sectorPercentage; private AmpClassificationConfiguration classificationConfig; + @Interchangeable(fieldTitle = "Indirect Sectors") + private Set indirectSectors = new HashSet<>(); + + public Set getIndirectSectors() { return indirectSectors; } + + public void setIndirectSectors(Set indirectSectors) { + this.indirectSectors = indirectSectors; + } + + public void addIndirectSector(AmpActivityIndirectSector pIndirectSector) { + pIndirectSector.setActivitySector(this); + indirectSectors.add(pIndirectSector); + } + + + + public AmpClassificationConfiguration getClassificationConfig() { return classificationConfig; } @@ -75,7 +94,7 @@ public Float getSectorPercentage() { public void setSectorPercentage(Float sectorPercentage) { this.sectorPercentage = sectorPercentage; } - + public String toString() { return sectorId!=null?sectorId.getName():""; } @@ -89,7 +108,7 @@ public boolean equalsForVersioning(Object obj) { } return false; } - + public Object getValue() { return this.sectorPercentage; } @@ -118,14 +137,20 @@ public Output getOutput() { out.getOutputs().add(new Output(null, new String[] { scheme + name + " - Percentage: "}, new Object[] { this.sectorPercentage })); return out; } - + @Override public Object prepareMerge(AmpActivityVersion newActivity) throws CloneNotSupportedException { AmpActivitySector aux = (AmpActivitySector) clone(); aux.activityId = newActivity; + + aux.setIndirectSectors(new HashSet()); + for(AmpActivityIndirectSector indirectSector : this.indirectSectors) { + AmpActivityIndirectSector auxIndirectSector = (AmpActivityIndirectSector) indirectSector.clone(); + auxIndirectSector.setId(null); + aux.addIndirectSector(auxIndirectSector); + } + aux.ampActivitySectorId = null; - //aux.sectorId = (AmpSector) aux.sectorId.clone(); - //this.sectorId.setAmpSectorId(null); return aux; } diff --git a/amp/WEB-INF/web.xml b/amp/WEB-INF/web.xml index bbd610f8459..30688699955 100644 --- a/amp/WEB-INF/web.xml +++ b/amp/WEB-INF/web.xml @@ -2,7 +2,7 @@ + version="3.0"> AMP 2.0 Aid Management Platform @@ -51,7 +51,7 @@ utf-8 - + populateTLSRequestFilter org.digijava.kernel.request.PopulateTLSRequestFilter @@ -62,7 +62,7 @@ REQUEST ERROR - + HibernateFilter org.digijava.kernel.startup.HibernateSessionRequestFilter @@ -96,10 +96,10 @@ REQUEST ERROR - + org.springframework.web.context.request.RequestContextListener - + org.digijava.kernel.startup.AmpSessionListener @@ -108,12 +108,12 @@ net.sf.ehcache.constructs.web.ShutdownListener - + log4jExposeWebAppRoot false - + patchAMP @@ -150,7 +150,7 @@ showMasterLayout /* - + org.digijava.module.aim.startup.AMPStartupListener @@ -206,6 +206,7 @@ asyncDetector /rest/activity/async/* /rest/activity/updateMappings/async/* + /rest/activity/updateSectorMappings/async/* @@ -272,14 +273,14 @@ /repository/help/struts-config.xml, /repository/contentrepository/struts-config.xml, /repository/categorymanager/struts-config.xml, /repository/sdm/struts-config.xml, - /repository/message/struts-config.xml, + /repository/message/struts-config.xml, /repository/parisindicator/struts-config.xml, /repository/search/struts-config.xml, /repository/content/struts-config.xml, /repository/xmlpatcher/struts-config.xml, /repository/esrigis/struts-config.xml, /repository/budgetexport/struts-config.xml, - /repository/gpi/struts-config.xml + /repository/gpi/struts-config.xml @@ -328,7 +329,7 @@ 403 /showLayout.do?layout=login - + 404 /exceptionHandle.do diff --git a/amp/repository/aim/module-config.xml b/amp/repository/aim/module-config.xml index c7bfcd335da..7883ef3a53e 100644 --- a/amp/repository/aim/module-config.xml +++ b/amp/repository/aim/module-config.xml @@ -272,6 +272,7 @@ org.digijava.module.aim.dbentity.AmpThemeMapping org.digijava.module.aim.dbentity.AmpSectorMapping + org.digijava.module.aim.dbentity.AmpActivityIndirectSector From 370efade2e2cbe7cb8963cf3e8959d064cddb68c Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Thu, 7 Dec 2023 19:13:44 -0300 Subject: [PATCH 05/85] AMP-30574 - Sector Mappings Job files --- .../jobs/IndirectSectorUpdaterJob.java | 101 ++++++++++++++++++ .../3.5.05.1/AMP-30574-indirect-sectors.xml | 20 ++++ 2 files changed, 121 insertions(+) create mode 100644 amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java create mode 100644 amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml diff --git a/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java b/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java new file mode 100644 index 00000000000..39907e235ce --- /dev/null +++ b/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java @@ -0,0 +1,101 @@ +package org.digijava.module.message.jobs; + +import org.apache.commons.lang3.mutable.MutableInt; +import org.dgfoundation.amp.onepager.util.ActivityUtil; +import org.dgfoundation.amp.onepager.util.SaveContext; +import org.digijava.kernel.persistence.PersistenceManager; +import org.digijava.kernel.request.Site; +import org.digijava.kernel.user.User; +import org.digijava.kernel.util.SiteUtils; +import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.startup.AMPStartupListener; +import org.digijava.module.aim.startup.AmpBackgroundActivitiesUtil; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * @author Diego Rossi + */ +public class IndirectSectorUpdaterJob implements Job { + + private final Logger logger = LoggerFactory.getLogger(IndirectSectorUpdaterJob.class); + + private final User user = new User(CloseExpiredActivitiesJob.AMP_MODIFIER_USER_EMAIL, + CloseExpiredActivitiesJob.AMP_MODIFIER_FIRST_NAME, + CloseExpiredActivitiesJob.AMP_MODIFIER_LAST_NAME); + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + try { + List activityIds = PersistenceManager.supplyInTransaction(this::loadActivityIds); + + logger.info("Activities to process: " + activityIds.size()); + MutableInt errors = new MutableInt(); + + activityIds.forEach(id -> { + try { + PersistenceManager.inTransaction(() -> loadAndSaveActivity(id)); + } catch (Exception e) { + errors.increment(); + logger.error("Failed to save activity " + id, e); + } + }); + + logger.info("Finished updating activities with {} errors.", errors); + } catch (Exception e) { + logger.error("Job failed", e); + } + } + + // TODO: Delete. It's only for testing purposes. + public List executeBis() { + return PersistenceManager.supplyInTransaction(this::loadActivityIds); + } + + @SuppressWarnings("unchecked") + private List loadActivityIds() { + return PersistenceManager.getSession() + .createCriteria(AmpActivity.class) + .createAlias("sectors", "sec") + .createAlias("sec.classificationConfig", "config") + .add(Restrictions.isNotNull("team")) + .add(Restrictions.eq("config.name", AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) + .setProjection(Projections.distinct(Projections.property("ampActivityId"))) + .list(); + } + + private void loadAndSaveActivity(Long id) { + AmpActivityVersion activity = (AmpActivityVersion) + PersistenceManager.getSession().load(AmpActivityVersion.class, id); + + AmpTeamMember member = getTeamMember(activity.getTeam(), user); + + saveActivity(activity, member); + } + + private AmpTeamMember getTeamMember(AmpTeam team, User user) { + try { + return AmpBackgroundActivitiesUtil.createActivityTeamMemberIfNeeded(team, user); + } catch (Exception e) { + throw new RuntimeException("Failed to obtain or create team member.", e); + } + } + + private void saveActivity(AmpActivityVersion activity, AmpTeamMember member) { + Site site = SiteUtils.getDefaultSite(); + Locale locale = new Locale("en"); + String path = AMPStartupListener.SERVLET_CONTEXT_ROOT_REAL_PATH; + Boolean draft = activity.getDraft(); + SaveContext context = SaveContext.job(); + ActivityUtil.saveActivity(activity, null, member, site, locale, path, draft, context); + } +} diff --git a/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml b/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml new file mode 100644 index 00000000000..5e8699aaa56 --- /dev/null +++ b/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml @@ -0,0 +1,20 @@ + + + AMP-30574 + drossi + Indirect Sectors Job + + + + From 257b4cc044878c2d57a35a09f29061d06e625135 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Tue, 12 Dec 2023 14:25:59 -0300 Subject: [PATCH 06/85] AMP-30574 - Sector Mappings Fix CRUD saving changes --- .../admin/sectorMapping/components/SectorsHeader.jsx | 6 ++++-- .../src/org/digijava/module/aim/util/SectorUtil.java | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx index abf2110b523..c778b566b39 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorsHeader.jsx @@ -25,7 +25,8 @@ class SectorsHeader extends Component { disabled={busy} placeholder={translations[`${trnPrefix}choose_src_scheme`]} label={translations[`${trnPrefix}src-scheme-sector`]} - options={schemes.filter(p => p.visible === true)} + // options={schemes.filter(p => p.visible === true)} + options={schemes.filter(p => p.classificationId !== null)} selected={src ? [src] : []} onChange={onChange.bind(null, TYPE_SRC)} /> @@ -35,7 +36,8 @@ class SectorsHeader extends Component { disabled={busy} placeholder={translations[`${trnPrefix}choose_dst_scheme`]} label={translations[`${trnPrefix}dst-scheme-sector`]} - options={schemes.filter(p => p.visible === false)} + // options={schemes.filter(p => p.visible === false)} + options={schemes.filter(p => p.classificationId !== null)} selected={dst ? [dst] : []} onChange={onChange.bind(null, TYPE_DST)} /> diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index eb1fabfc020..6a18ba6d350 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -1296,6 +1296,7 @@ public static void createSectorMappings(List sectorMappings) t for (AmpSectorMapping em : existingMappings) { session.delete(em); } + session.flush(); for (AmpSectorMapping asm : sectorMappings) { session.save(asm); } From a9842a6ef124f5678c2fff67baa3abc1dd02af55 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Wed, 13 Dec 2023 12:31:46 -0300 Subject: [PATCH 07/85] AMP-30574 - Sector Mappings Cleaning of files and functionalities related with "Update all activities" and Job updater --- .../sectorMapping/actions/updateActivities.js | 76 ------------- .../actions/updateActivitiesAction.js | 28 ----- .../sectorMapping/components/FormSectors.jsx | 57 ++-------- .../components/HeaderActions.jsx | 7 +- .../admin/sectorMapping/components/Main.jsx | 7 +- .../components/SectorMappingTable.jsx | 4 +- .../sectorMapping/constants/Constants.jsx | 1 - .../sectorMapping/reducers/rootReducer.js | 4 +- .../reducers/updateActivitiesReducer.js | 46 -------- .../sector/config/initialTranslations.json | 4 +- .../amp/onepager/util/ActivityUtil.java | 5 - .../onepager/util/IndirectSectorUpdater.java | 78 -------------- .../activity/InterchangeEndpoints.java | 28 ----- ...cActivityIndirectSectorUpdaterService.java | 79 -------------- .../AmpActivityIndirectSector.hbm.xml | 26 ----- .../dbentity/AmpActivityIndirectSector.java | 71 ------------ .../aim/dbentity/AmpActivitySector.hbm.xml | 7 -- .../aim/dbentity/AmpActivitySector.java | 29 ++--- .../jobs/IndirectSectorUpdaterJob.java | 101 ------------------ amp/WEB-INF/web.xml | 1 - amp/repository/aim/module-config.xml | 1 - .../3.5.05.1/AMP-30574-indirect-sectors.xml | 20 ---- 22 files changed, 27 insertions(+), 653 deletions(-) delete mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js delete mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js delete mode 100644 amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js delete mode 100644 amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java delete mode 100644 amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java delete mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml delete mode 100644 amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java delete mode 100644 amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java delete mode 100644 amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js deleted file mode 100644 index a33f5157e8a..00000000000 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivities.js +++ /dev/null @@ -1,76 +0,0 @@ -import { - invokeUpdateActivities, - updateActivitiesPending, - updateActivitiesError, - updateActivitiesSuccess -} from './updateActivitiesAction'; -import { UPDATE_ACTIVITIES_EP } from '../constants/Constants'; - -let timer; -const GET = 'GET'; - -function scheduleTimer(url, dispatch) { - clearInterval(timer); - timer = setInterval(() => checkForUpdate(url, dispatch), 1000); -} - -function checkForUpdate(url, dispatch) { - fetch(url).then(layoutRes => { - if (layoutRes.status === 200) { - return layoutRes.headers.get('X-Async-Status'); - } else { - return layoutRes.json(); - } - }).then((result) => { - if (result === null) { - clearInterval(timer); - return dispatch(updateActivitiesSuccess()); - } else if (!result.error) { - if (result === 'RUNNING') { - // its running do nothing - } else { - dispatch(updateActivitiesError({ msg: `result ${result}` })); - clearInterval(timer); - } - } else { - dispatch(updateActivitiesError({ msg: 'result false' })); - clearInterval(timer); - } - return result; - }).catch(error => { - dispatch(updateActivitiesError(error)); - }); -} - -function updateActivities() { - return dispatch => { - dispatch(updateActivitiesPending()); - const requestOptions = { - method: GET, - headers: { Prefer: 'respond-async' } - }; - fetch(UPDATE_ACTIVITIES_EP, requestOptions).then(layoutRes => { - if (layoutRes.status === 200) { - return layoutRes.headers.get('location'); - } else { - return layoutRes.json(); - } - }).then((result) => { - if (!result.error) { - dispatch(invokeUpdateActivities()); - scheduleTimer(result, dispatch); - // TODO better process generic errors coming from the API - } else if (result.error['0212']) { - const msg = result.error['0212'][0].PROCESS_ALREADY_RUNNING[0]; - dispatch(updateActivitiesError({ msg })); - } else { - dispatch(updateActivitiesError({ msg: 'result false' })); - } - return result; - }).catch(error => { - dispatch(updateActivitiesError(error)); - }); - }; -} - -export default updateActivities; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js deleted file mode 100644 index 71cd05bf391..00000000000 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/actions/updateActivitiesAction.js +++ /dev/null @@ -1,28 +0,0 @@ -export const UPDATE_ACTIVITIES_PENDING = 'UPDATE_ACTIVITIES_PENDING'; -export const UPDATE_ACTIVITIES_SUCCESS = 'UPDATE_ACTIVITIES_SUCCESS'; -export const UPDATE_ACTIVITIES_ERROR = 'UPDATE_ACTIVITIES_ERROR'; -export const INVOKE_ACTIVITIES_SUCCESS = 'INVOKE_ACTIVITIES_SUCCESS'; - -export function updateActivitiesPending() { - return { - type: UPDATE_ACTIVITIES_PENDING - }; -} - -export function invokeUpdateActivities() { - return { - type: INVOKE_ACTIVITIES_SUCCESS - }; -} -export function updateActivitiesSuccess() { - return { - type: UPDATE_ACTIVITIES_SUCCESS - }; -} - -export function updateActivitiesError(error) { - return { - type: UPDATE_ACTIVITIES_ERROR, - error - }; -} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx index 778b6ce1b63..26d7e622744 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/FormSectors.jsx @@ -21,9 +21,7 @@ import { sendSectorMappingPending, sendSectorMappingSaving } from "../reducers/saveSectorMappingReducer"; -import {updateActivitiesError, updateActivitiesPending} from "../reducers/updateActivitiesReducer"; import saveSectorMappings from "../actions/saveSectorMappings"; -import updateActivities from "../actions/updateActivities"; import SectorsHeader from "./SectorsHeader"; import Notifications from "./Notifications"; import HeaderActions from "./HeaderActions"; @@ -54,7 +52,7 @@ class FormSectors extends Component { } componentDidMount() { - const { mappings, schemes, translations, trnPrefix, settings, isIndirect } = this.context; + const { mappings, schemes, translations, trnPrefix } = this.context; document.title = translations[`${trnPrefix}page-title`]; // Load Source Scheme Sector selected @@ -178,22 +176,6 @@ class FormSectors extends Component { }); } - onUpdateActivities = () => { - const { _updateActivities, translations } = this.props; - const { trnPrefix } = this.context; - const { data } = this.state; - const validateMappings = Validation.checkMappings(data); - if (validateMappings === 0) { - if (window.confirm(translations[`${trnPrefix}button-update-activities-confirmation`])) { - this.clearMessages(); - this.setState({ blockUI: true }); - _updateActivities(); - } - } else { - this.setState({ validationErrors: translations[`${trnPrefix}validation_error_${validateMappings}`] }); - } - } - saveAll() { const { data, src, dst } = this.state; const { _saveSectorMappings, translations } = this.props; @@ -279,8 +261,8 @@ class FormSectors extends Component { } render() { - const { data, validationErrors, src, dst, updatedActivities, unsavedChanges, saved } = this.state; - const { error, pending, translations, updating, errorUpdating, saving } = this.props; + const { data, validationErrors, src, dst, unsavedChanges, saved } = this.state; + const { error, pending, translations, saving } = this.props; const { trnPrefix } = this.context; const messages = []; @@ -293,18 +275,6 @@ class FormSectors extends Component { if (!saving && !error && !unsavedChanges && saved) { messages.push({ isError: false, text: translations[`${trnPrefix}notification-saved-ok`] }); } - if (updatedActivities) { - messages.push({ isError: false, text: translations[`${trnPrefix}update-activities-successful`] }); - } - if (updating) { - messages.push({ isError: false, text: translations[`${trnPrefix}update-activities-wait`] }); - } - if (errorUpdating) { - messages.push({ - isError: true, - text: errorUpdating - }); - } return (
@@ -312,17 +282,16 @@ class FormSectors extends Component { src={src} dst={dst} key={Math.random()} - busy={updating} + busy={false} onChange={this.onChangeMainScheme} /> 0} /> @@ -334,7 +303,7 @@ class FormSectors extends Component { remove={this.remove} src={src} dst={dst} - busy={updating}/> + />
); } @@ -347,30 +316,22 @@ FormSectors.propTypes = { translations: PropTypes.object.isRequired, error: PropTypes.object, pending: PropTypes.bool, - updating: PropTypes.bool, - errorUpdating: PropTypes.string, _saveSectorMappings: PropTypes.func.isRequired, - _updateActivities: PropTypes.func.isRequired, saving: PropTypes.bool.isRequired }; FormSectors.defaultProps = { error: undefined, - pending: false, - updating: false, - errorUpdating: null + pending: false }; const mapStateToProps = state => ({ translations: state.translationsReducer.translations, error: sendSectorMappingError(state.saveSectorMappingReducer), saving: sendSectorMappingSaving(state.saveSectorMappingReducer), - pending: sendSectorMappingPending(state.saveSectorMappingReducer), - updating: updateActivitiesPending(state.updateActivitiesReducer), - errorUpdating: updateActivitiesError(state.updateActivitiesReducer) + pending: sendSectorMappingPending(state.saveSectorMappingReducer) }); const mapDispatchToProps = dispatch => bindActionCreators({ - _saveSectorMappings: saveSectorMappings, - _updateActivities: updateActivities + _saveSectorMappings: saveSectorMappings }, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(FormSectors); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx index 600fd9efc3c..24134c703ae 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/HeaderActions.jsx @@ -8,7 +8,7 @@ import './css/style.css'; class HeaderActions extends Component { render() { const { - translations, onAddRow, onSaveAll, onRevertAll, src, dst, onUpdateActivities, busy, dataPresent, unsavedChanges + translations, onAddRow, onSaveAll, onRevertAll, src, dst, busy, dataPresent, unsavedChanges } = this.props; const {trnPrefix} = this.context; @@ -37,10 +37,6 @@ class HeaderActions extends Component { disabled={busy || !unsavedChanges}> {translations[`${trnPrefix}button-revert-all-edits`]} - @@ -56,7 +52,6 @@ HeaderActions.propTypes = { onSaveAll: PropTypes.func.isRequired, onRevertAll: PropTypes.func.isRequired, translations: PropTypes.object.isRequired, - onUpdateActivities: PropTypes.func.isRequired, src: PropTypes.object, dst: PropTypes.object, busy: PropTypes.bool.isRequired, diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx index 34fd0bf362a..3509a53bd22 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Main.jsx @@ -33,7 +33,6 @@ class Main extends Component { if (layout && layout.logged && layout.administratorMode === true) { _fetchSectorMappings(api.mappingConfig); _fetchSchemes(api.allSchemes); - //this.setState({ isSuperAdmin: layout.email.indexOf('super') === 0, settings }); } else { window.location.replace('/login.do'); } @@ -47,7 +46,7 @@ class Main extends Component { } render() { - const { mappings, schemes, api, trnPrefix, indirectSectorUpdatePending } = this.props; + const { mappings, schemes, api, trnPrefix } = this.props; const { translations } = this.context; const { isSuperAdmin, settings } = this.state; @@ -64,7 +63,6 @@ class Main extends Component { - ); @@ -89,8 +87,7 @@ const mapStateToProps = state => ({ schemes: getSchemes(state.startupReducer), pendingMappings: getSectorMappingPending(state.startupReducer), pendingSchemes: getSchemesPending(state.startupReducer), - translations: state.translationsReducer.translations, - indirectSectorUpdatePending: state.updateActivitiesReducer.indirectSectorUpdatePending + translations: state.translationsReducer.translations }); const mapDispatchToProps = dispatch => bindActionCreators({ diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx index 7770a0d8957..87b465d9c00 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/SectorMappingTable.jsx @@ -11,7 +11,7 @@ import SectorMappingTableRow from "./SectorMappingTableRow"; class SectorMappingTable extends Component { render() { - const { list, translations, onChange, remove, dst, src, busy } = this.props; + const { list, translations, onChange, remove, dst, src } = this.props; const { trnPrefix } = this.context; return ( @@ -41,7 +41,7 @@ class SectorMappingTable extends Component { remove={remove} dst={dst} src={src} - disabled={busy} /> + disabled={false} /> ))} diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx index 6ba1e9cd166..35ef1a03149 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/constants/Constants.jsx @@ -12,4 +12,3 @@ export const TYPE_SRC = 'src'; export const TYPE_DST = 'dst'; export const LAYOUT_EP = '/rest/security/layout'; export const SETTINGS_EP = '/rest/amp/settings'; -export const UPDATE_ACTIVITIES_EP = '/rest/activity/updateSectorMappings/async'; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js index 8296ad3ea94..b91c9d348d8 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/rootReducer.js @@ -2,11 +2,9 @@ import { combineReducers } from 'redux'; import startupReducer from './startupReducer'; import saveSectorMappingReducer from './saveSectorMappingReducer'; import translationsReducer from '../../../../utils/reducers/translationsReducer'; -import updateActivitiesReducer from './updateActivitiesReducer'; export default combineReducers({ startupReducer, translationsReducer, - saveSectorMappingReducer, - updateActivitiesReducer + saveSectorMappingReducer }); diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js deleted file mode 100644 index db08c5a6053..00000000000 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/reducers/updateActivitiesReducer.js +++ /dev/null @@ -1,46 +0,0 @@ -import { - UPDATE_ACTIVITIES_PENDING, UPDATE_ACTIVITIES_SUCCESS, UPDATE_ACTIVITIES_ERROR, INVOKE_ACTIVITIES_SUCCESS -} from '../actions/updateActivitiesAction'; - -const initialState = { - updating: false, - error: null, - indirectSectorUpdatePending: false -}; - -export default function updateActivitiesReducer(state = initialState, action) { - switch (action.type) { - case UPDATE_ACTIVITIES_PENDING: - return { - ...state, - updating: true, - error: null - }; - case INVOKE_ACTIVITIES_SUCCESS: - return { - ...state, - updating: false, - error: null, - indirectSectorUpdatePending: true - }; - case UPDATE_ACTIVITIES_SUCCESS: { - return { - ...state, - indirectSectorUpdatePending: false - }; - } - case UPDATE_ACTIVITIES_ERROR: - return { - ...state, - updating: false, - error: action.error.msg, - indirectSectorUpdatePending: false - }; - default: - return state; - } -} - -export const updateActivities = state => state; -export const updateActivitiesPending = state => state.updating; -export const updateActivitiesError = state => state.error; diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json index 0042642b3fe..f5b9e5b9403 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json @@ -30,7 +30,5 @@ "amp.admin.sectorMapping:choose_src_scheme": "Choose a Source Scheme", "amp.admin.sectorMapping:choose_dst_scheme": "Choose a Destination Scheme", "amp.admin.sectorMapping:choose_src_sector": "Choose a Source Sector", - "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector", - "amp.admin.sectorMapping:update-activities-successful": "Activities where successfully updated", - "amp.admin.sectorMapping:update-activities-wait": "Updating activities, please wait..." + "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector" } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java index 41dc0f8a276..feddf866753 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/ActivityUtil.java @@ -335,7 +335,6 @@ public static AmpActivityVersion saveActivityNewVersion(AmpActivityVersion a, } updateIndirectPrograms(a, session); - updateIndirectSectors(a, session); logAudit(ampCurrentMember, a, newActivity); @@ -352,10 +351,6 @@ private static void updateIndirectPrograms(AmpActivityVersion a, Session session new IndirectProgramUpdater().updateIndirectPrograms(a, session); } - private static void updateIndirectSectors(AmpActivityVersion a, Session session) { - new IndirectSectorUpdater().updateIndirectSectors(a, session); - } - public static boolean detectDraftChange(AmpActivityVersion a, boolean draft) { return Boolean.TRUE.equals(a.getDraft()) != draft; } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java deleted file mode 100644 index f66c1ec1ca4..00000000000 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/util/IndirectSectorUpdater.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.dgfoundation.amp.onepager.util; - -import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.module.aim.dbentity.*; -import org.digijava.module.aim.util.ActivityUtil; -import org.digijava.module.aim.util.activity.ActivityCloser; -import org.digijava.module.aim.util.activity.GenericUserHelper; -import org.hibernate.Session; - -import java.math.BigDecimal; -import java.util.*; - -import static java.util.Collections.emptySet; -import static java.util.stream.Collectors.*; - -/** - * @author Diego Rossi - */ -public class IndirectSectorUpdater { - - public AmpActivityVersion updateIndirectSectors(AmpActivityVersion activity, Session session) { - Map> mapping = loadMapping(session); - updateIndirectSectors(activity, mapping); - return activity; - } - - public void updateIndirectSectorMapping(Long ampActivityId) { - PersistenceManager.inTransaction(() -> { - try { - AmpActivityVersion o = ActivityUtil.loadActivity(ampActivityId); - ActivityCloser.cloneActivity(GenericUserHelper.getAmpTeamMemberModifier(o.getTeam()), - o, SaveContext.admin()); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - } - - @SuppressWarnings("unchecked") - private Map> loadMapping(Session session) { - List list = session - .createCriteria(AmpSectorMapping.class) - .setCacheable(true) - .list(); - - return list.stream().collect(groupingBy( - AmpSectorMapping::getSrcSector, - () -> new TreeMap<>(Comparator.comparing(AmpSector::getAmpSectorId)), - mapping(AmpSectorMapping::getDstSector, toCollection(this::newSetComparingById)))); - } - - private Set newSetComparingById() { - return new TreeSet<>(Comparator.comparing(AmpSector::getAmpSectorId)); - } - - private void updateIndirectSectors(AmpActivityVersion activity, Map> mapping) { - activity.getSectors().forEach((as) -> { - List indirectSectors = new ArrayList<>(mapping.getOrDefault(as.getSectorId(), emptySet())); - as.getIndirectSectors().clear(); - - if(!indirectSectors.isEmpty()) { - PercentagesUtil.SplitResult sr; - if (as.getSectorPercentage() != null) { - BigDecimal sum = PercentagesUtil.closestTo(as.getSectorPercentage(), - AmpActivityIndirectSector.PERCENTAGE_PRECISION); - sr = PercentagesUtil.split(sum, indirectSectors.size(), - AmpActivityIndirectSector.PERCENTAGE_PRECISION); - } else { sr = null; } - - for (int i = 0; i < indirectSectors.size(); i++) { - as.addIndirectSector(new AmpActivityIndirectSector(indirectSectors.get(i), - sr == null ? null : sr.getValueFor(i))); - } - } - }); - } - -} diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java index f7a04f85421..036dd07a0b6 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/activity/InterchangeEndpoints.java @@ -62,7 +62,6 @@ import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.X_ASYNC_RESULT_ID; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.X_ASYNC_STATUS; import static org.digijava.kernel.ampapi.endpoints.async.AsyncActivityIndirectProgramUpdaterService.PROGRAM_UPDATER_KEY; -import static org.digijava.kernel.ampapi.endpoints.async.AsyncActivityIndirectSectorUpdaterService.SECTOR_UPDATER_KEY; /** @@ -565,33 +564,6 @@ public Response getUpdateMappingsResult(@PathParam("result-id") String resultId) return buildResultId(PROGRAM_UPDATER_KEY + resultId); } - - @GET - @Path("/updateSectorMappings/async") - @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") - @ApiMethod(authTypes = AuthRule.IN_ADMIN, id = "updateSectorMappings", ui = false) - public Response updateSectorMappings() { - - String resultId = (String) TLSUtils.getRequest().getAttribute(X_ASYNC_RESULT_ID); - if (resultId == null) { - ApiErrorResponseService.reportError(BAD_REQUEST, ActivityErrors.ONLY_SYNC - .withDetails("Only sync process is allowed")); - } - if (!AsyncResultCacher.canAddAnotherUnique(SECTOR_UPDATER_KEY)) { - ApiErrorResponseService.reportError(BAD_REQUEST, ActivityErrors.PROCESS_ALREADY_RUNNING - .withDetails("Only one process at a time is allowed")); - } else { - // Start the process - AsyncActivityIndirectSectorUpdaterService.getInstance(). - updateIndirectSectors(SECTOR_UPDATER_KEY + resultId); - } - String location = String.format("%s/result/%s", UrlUtils.buildRequestUrl(TLSUtils.getRequest()), resultId); - return Response.ok() - .header("location", location) - .build(); - } - - private Response buildResultId(String resultId) { AsyncResult asyncResult = AsyncResultCacher.getAsyncResult(resultId); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java deleted file mode 100644 index f56916485af..00000000000 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/async/AsyncActivityIndirectSectorUpdaterService.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.digijava.kernel.ampapi.endpoints.async; - -import org.dgfoundation.amp.ar.viewfetcher.SQLUtils; -import org.dgfoundation.amp.onepager.util.IndirectSectorUpdater; -import org.digijava.kernel.ampapi.endpoints.common.JsonApiResponse; -import org.digijava.kernel.ampapi.endpoints.errors.ApiError; -import org.digijava.kernel.ampapi.endpoints.errors.ApiErrorMessage; -import org.digijava.kernel.ampapi.endpoints.errors.GenericErrors; -import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.kernel.request.TLSUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; - -public class AsyncActivityIndirectSectorUpdaterService { - public static final String SECTOR_UPDATER_KEY = "ASYNC_ACTIVITY_INDIRECT_SECTOR_UPDATER_KEY"; - private static AsyncActivityIndirectSectorUpdaterService instance; - public static AsyncActivityIndirectSectorUpdaterService getInstance() { - if (instance == null) { - instance = new AsyncActivityIndirectSectorUpdaterService(); - } - return instance; - } - - public void updateIndirectSectors(String key) { - - AsyncResultCacher.addAsyncResultsData(key, new AsyncResult(key)); - - ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1); - executor.execute(() -> { - final List ampActivityIds = new ArrayList<>(); - PersistenceManager.inTransaction(() -> { - TLSUtils.populateMockTlsUtils(); - - PersistenceManager.getSession().doWork(conn -> { - - String query = " SELECT DISTINCT actSec.amp_activity_id " + - " FROM amp_sector_mapping secMap " + - " JOIN amp_activity_sector actSec ON secMap.src_sector_id = actSec.amp_sector_id " + - " WHERE EXISTS( " + - " SELECT 1 " + - " FROM amp_activity a, " + - " amp_activities_categoryvalues aacv, " + - " amp_category_class acc, " + - " amp_category_value acv, " + - " amp_global_settings gs " + - " WHERE a.amp_activity_id = aacv.amp_activity_id " + - " AND acv.amp_category_class_id = acc.id " + - " AND acc.keyname = 'activity_status' " + - " AND acv.id = aacv.amp_categoryvalue_id " + - " AND gs.settingsvalue::BIGINT = acv.id " + - " AND gs.settingsname = 'Closed activity status' " + - " AND actSec.amp_activity_id = a.amp_activity_id )"; - ampActivityIds.addAll(SQLUtils.fetchLongs(conn, query)); - }); - }); - final List errors = new ArrayList<>(); - Map details = new ConcurrentHashMap<>(); - - for (Long ampActivityId : ampActivityIds) { - try { - (new IndirectSectorUpdater()).updateIndirectSectorMapping(ampActivityId); - details.put(ampActivityId + "", "Activity Updated"); - } catch (Exception e) { - errors.add(GenericErrors.INTERNAL_ERROR.withDetails(ampActivityId - + "failed with error" + e.toString())); - } - } - AsyncResultCacher.getAsyncResult(key).getResults().add(new JsonApiResponse(ApiError.toError(errors), - details)); - AsyncResultCacher.getAsyncResult(key).setStatus(AsyncStatus.DONE); - }); - executor.shutdown(); - } -} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml deleted file mode 100644 index e7f82249f1e..00000000000 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.hbm.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - AMP_ACTIVITY_INDIRECT_SECTOR_SEQ - - - - - - - - - - - - diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java deleted file mode 100644 index 1ce91422658..00000000000 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivityIndirectSector.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.digijava.module.aim.dbentity; - -import org.digijava.module.aim.annotations.interchange.Interchangeable; -import org.digijava.module.aim.annotations.interchange.InterchangeableId; -import java.io.Serializable; -import java.math.BigDecimal; - -/** - * @author Diego Rossi - */ -public class AmpActivityIndirectSector implements Serializable, Cloneable { - - public static final int PERCENTAGE_PRECISION = 2; - - @InterchangeableId - @Interchangeable(fieldTitle = "Id") - private Long id; - - private AmpActivitySector activitySector; - - @Interchangeable(fieldTitle = "Sector", pickIdOnly = true) - private AmpSector sector; - - @Interchangeable(fieldTitle = "Percentage") - private BigDecimal percentage; - - public AmpActivityIndirectSector() { - } - - public AmpActivityIndirectSector(AmpSector sector, BigDecimal percentage) { - this.sector = sector; - this.percentage = percentage; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public AmpActivitySector getActivitySector() { - return activitySector; - } - - public void setActivitySector(AmpActivitySector activitySector) { - this.activitySector = activitySector; - } - - public BigDecimal getPercentage() { - return percentage; - } - - public void setPercentage(BigDecimal percentage) { - this.percentage = percentage; - } - - public AmpSector getSector() { - return sector; - } - - public void setSector(AmpSector sector) { - this.sector = sector; - } - - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } -} diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml index 04a5cf02964..90b2297051a 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.hbm.xml @@ -22,13 +22,6 @@ - - - - - - - diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java index 108f77bb182..928eb69db73 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java @@ -38,19 +38,19 @@ public class AmpActivitySector implements Versionable, Serializable, Cloneable, private AmpClassificationConfiguration classificationConfig; - @Interchangeable(fieldTitle = "Indirect Sectors") - private Set indirectSectors = new HashSet<>(); +// @Interchangeable(fieldTitle = "Indirect Sectors") +// private Set indirectSectors = new HashSet<>(); - public Set getIndirectSectors() { return indirectSectors; } + //public Set getIndirectSectors() { return indirectSectors; } - public void setIndirectSectors(Set indirectSectors) { - this.indirectSectors = indirectSectors; - } - - public void addIndirectSector(AmpActivityIndirectSector pIndirectSector) { - pIndirectSector.setActivitySector(this); - indirectSectors.add(pIndirectSector); - } +// public void setIndirectSectors(Set indirectSectors) { +// this.indirectSectors = indirectSectors; +// } +// +// public void addIndirectSector(AmpActivityIndirectSector pIndirectSector) { +// pIndirectSector.setActivitySector(this); +// indirectSectors.add(pIndirectSector); +// } @@ -143,13 +143,6 @@ public Object prepareMerge(AmpActivityVersion newActivity) throws CloneNotSuppor AmpActivitySector aux = (AmpActivitySector) clone(); aux.activityId = newActivity; - aux.setIndirectSectors(new HashSet()); - for(AmpActivityIndirectSector indirectSector : this.indirectSectors) { - AmpActivityIndirectSector auxIndirectSector = (AmpActivityIndirectSector) indirectSector.clone(); - auxIndirectSector.setId(null); - aux.addIndirectSector(auxIndirectSector); - } - aux.ampActivitySectorId = null; return aux; } diff --git a/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java b/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java deleted file mode 100644 index 39907e235ce..00000000000 --- a/amp/WEB-INF/src/org/digijava/module/message/jobs/IndirectSectorUpdaterJob.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.digijava.module.message.jobs; - -import org.apache.commons.lang3.mutable.MutableInt; -import org.dgfoundation.amp.onepager.util.ActivityUtil; -import org.dgfoundation.amp.onepager.util.SaveContext; -import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.kernel.request.Site; -import org.digijava.kernel.user.User; -import org.digijava.kernel.util.SiteUtils; -import org.digijava.module.aim.dbentity.*; -import org.digijava.module.aim.startup.AMPStartupListener; -import org.digijava.module.aim.startup.AmpBackgroundActivitiesUtil; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.quartz.Job; -import org.quartz.JobExecutionContext; -import org.quartz.JobExecutionException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * @author Diego Rossi - */ -public class IndirectSectorUpdaterJob implements Job { - - private final Logger logger = LoggerFactory.getLogger(IndirectSectorUpdaterJob.class); - - private final User user = new User(CloseExpiredActivitiesJob.AMP_MODIFIER_USER_EMAIL, - CloseExpiredActivitiesJob.AMP_MODIFIER_FIRST_NAME, - CloseExpiredActivitiesJob.AMP_MODIFIER_LAST_NAME); - - @Override - public void execute(JobExecutionContext context) throws JobExecutionException { - try { - List activityIds = PersistenceManager.supplyInTransaction(this::loadActivityIds); - - logger.info("Activities to process: " + activityIds.size()); - MutableInt errors = new MutableInt(); - - activityIds.forEach(id -> { - try { - PersistenceManager.inTransaction(() -> loadAndSaveActivity(id)); - } catch (Exception e) { - errors.increment(); - logger.error("Failed to save activity " + id, e); - } - }); - - logger.info("Finished updating activities with {} errors.", errors); - } catch (Exception e) { - logger.error("Job failed", e); - } - } - - // TODO: Delete. It's only for testing purposes. - public List executeBis() { - return PersistenceManager.supplyInTransaction(this::loadActivityIds); - } - - @SuppressWarnings("unchecked") - private List loadActivityIds() { - return PersistenceManager.getSession() - .createCriteria(AmpActivity.class) - .createAlias("sectors", "sec") - .createAlias("sec.classificationConfig", "config") - .add(Restrictions.isNotNull("team")) - .add(Restrictions.eq("config.name", AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) - .setProjection(Projections.distinct(Projections.property("ampActivityId"))) - .list(); - } - - private void loadAndSaveActivity(Long id) { - AmpActivityVersion activity = (AmpActivityVersion) - PersistenceManager.getSession().load(AmpActivityVersion.class, id); - - AmpTeamMember member = getTeamMember(activity.getTeam(), user); - - saveActivity(activity, member); - } - - private AmpTeamMember getTeamMember(AmpTeam team, User user) { - try { - return AmpBackgroundActivitiesUtil.createActivityTeamMemberIfNeeded(team, user); - } catch (Exception e) { - throw new RuntimeException("Failed to obtain or create team member.", e); - } - } - - private void saveActivity(AmpActivityVersion activity, AmpTeamMember member) { - Site site = SiteUtils.getDefaultSite(); - Locale locale = new Locale("en"); - String path = AMPStartupListener.SERVLET_CONTEXT_ROOT_REAL_PATH; - Boolean draft = activity.getDraft(); - SaveContext context = SaveContext.job(); - ActivityUtil.saveActivity(activity, null, member, site, locale, path, draft, context); - } -} diff --git a/amp/WEB-INF/web.xml b/amp/WEB-INF/web.xml index 30688699955..8f407f26c5f 100644 --- a/amp/WEB-INF/web.xml +++ b/amp/WEB-INF/web.xml @@ -206,7 +206,6 @@ asyncDetector /rest/activity/async/* /rest/activity/updateMappings/async/* - /rest/activity/updateSectorMappings/async/* diff --git a/amp/repository/aim/module-config.xml b/amp/repository/aim/module-config.xml index 7883ef3a53e..c7bfcd335da 100644 --- a/amp/repository/aim/module-config.xml +++ b/amp/repository/aim/module-config.xml @@ -272,7 +272,6 @@ org.digijava.module.aim.dbentity.AmpThemeMapping org.digijava.module.aim.dbentity.AmpSectorMapping - org.digijava.module.aim.dbentity.AmpActivityIndirectSector diff --git a/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml b/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml deleted file mode 100644 index 5e8699aaa56..00000000000 --- a/amp/xmlpatches/3.5.05.1/AMP-30574-indirect-sectors.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - AMP-30574 - drossi - Indirect Sectors Job - - - - From 131c9f6c1144d78baf0c20cf8300d69d046fa9d4 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Fri, 15 Dec 2023 19:08:25 -0300 Subject: [PATCH 08/85] AMP-30574 - Sector Mappings Functionality in Activity Form --- .../AmpSectorsFormSectionFeature.java | 53 ++++++-- .../tables/AmpSectorsFormTableFeature.java | 118 ++++++++++++------ .../ISectorTableDeleteListener.java | 8 ++ .../ISectorTableUpdateListener.java | 8 ++ .../onepager/models/AmpSectorSearchModel.java | 69 ++++++++-- .../aim/dbentity/AmpActivitySector.java | 17 --- 6 files changed, 196 insertions(+), 77 deletions(-) create mode 100644 amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java create mode 100644 amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableUpdateListener.java diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 015f83815ff..39bb528e6b7 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -4,29 +4,33 @@ */ package org.dgfoundation.amp.onepager.components.features.sections; -import java.util.Arrays; -import java.util.Comparator; import java.util.List; +import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.model.IModel; +import org.apache.wicket.request.cycle.RequestCycle; import org.dgfoundation.amp.onepager.components.features.tables.AmpSectorsFormTableFeature; import org.dgfoundation.amp.onepager.util.AmpFMTypes; import org.digijava.module.aim.dbentity.AmpActivityVersion; import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; +import org.digijava.module.aim.dbentity.AmpSector; import org.digijava.module.aim.util.SectorUtil; +import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; + /** * @author mpostelnicu@dgateway.org * since Oct 20, 2010 */ -public class AmpSectorsFormSectionFeature extends AmpFormSectionFeaturePanel { +public class AmpSectorsFormSectionFeature extends AmpFormSectionFeaturePanel + implements ISectorTableUpdateListener { - /** - * - */ private static final long serialVersionUID = -5601918041949098629L; + private AmpSectorsFormTableFeature primarySectorsTable; + private AmpSectorsFormTableFeature secondarySectorsTable; + /** * @param id * @param fmName @@ -36,18 +40,45 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel allClassificationConfigs = SectorUtil.getAllClassificationConfigsOrdered(); - for (AmpClassificationConfiguration sectorConf : allClassificationConfigs) { - AmpSectorsFormTableFeature sectorsTable=new AmpSectorsFormTableFeature(view.newChildId(), sectorConf.getName()+" Sectors", am,sectorConf); - view.add(sectorsTable); + AmpClassificationConfiguration primaryConf = new AmpClassificationConfiguration(); + AmpClassificationConfiguration secondaryConf = new AmpClassificationConfiguration(); + for (AmpClassificationConfiguration conf : allClassificationConfigs) { + if (conf.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { + primaryConf = conf; + } else if (conf.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + secondaryConf = conf; + } + } + + primarySectorsTable = new AmpSectorsFormTableFeature(view.newChildId(), + primaryConf.getName() + " Sectors", am, primaryConf); + primarySectorsTable.setUpdateListener(this); + + secondarySectorsTable = new AmpSectorsFormTableFeature(view.newChildId(), + secondaryConf.getName() + " Sectors", am, secondaryConf); + + view.add(primarySectorsTable); + view.add(secondarySectorsTable); + } + + + @Override + public void onUpdate(List data) { + if (secondarySectorsTable != null) { + // Update user interface if the request is ajax + AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); + if (target != null) { + secondarySectorsTable.updateBasedOnData(data); + target.add(secondarySectorsTable.getSearchSectors()); + } } - } } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index ed006c21593..84036f76b7f 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -41,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.MAXIMUM_PERCENTAGE; @@ -50,6 +51,50 @@ public class AmpSectorsFormTableFeature extends AmpFormTableFeaturePanel { + private ISectorTableUpdateListener updateListener; + + public void setUpdateListener(ISectorTableUpdateListener listener) { + this.updateListener = listener; + } + + private AmpAutocompleteFieldPanel searchSectors; + public AmpAutocompleteFieldPanel getSearchSectors() { + return this.searchSectors; + } + + /** + * Triggers an update event with the selected sectors based on the given sector classification. + * Method called when change the list of selected sectors to update (from the parent component) + * the another list of sectors. + * Is usefully when exist a mapping between sectors from different classifications. + * + * @param selectedSectors the set of selected sectors + * @param sectorClassification the current sector classification configuration + */ + protected void triggerUpdateEvent(Set selectedSectors, + AmpClassificationConfiguration sectorClassification) { + if (updateListener != null) { + List sectorsByClassification = new ArrayList(); + for (AmpActivitySector ampActivitySector : selectedSectors) { + if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) + sectorsByClassification.add(ampActivitySector.getSectorId()); + } + + updateListener.onUpdate(sectorsByClassification); + } + } + + /** + * Updates the component based on the given data. + * This method is called from the parent component when the data is updated. + * + * @param selectedSectors the list of selected sectors + */ + public void updateBasedOnData(List selectedSectors) { + this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED, selectedSectors); + } + + /** * @param id * @param fmName @@ -61,13 +106,11 @@ public AmpSectorsFormTableFeature(String id, String fmName, final AmpClassificationConfiguration sectorClassification) throws Exception { super(id, am, fmName, false, true); - final IModel> setModel = new PropertyModel>( - am, "sectors"); - if (setModel.getObject() == null) - setModel.setObject(new HashSet()); - if (sectorClassification.getDescription() != null) - addInfoText(sectorClassification.getDescription()); - + final IModel> setModel = new PropertyModel>(am, "sectors"); + if (setModel.getObject() == null) setModel.setObject(new HashSet()); + if (sectorClassification.getDescription() != null) addInfoText(sectorClassification.getDescription()); + + final IModel> listModel = new AbstractReadOnlyModel>() { private static final long serialVersionUID = 1L; @@ -78,23 +121,22 @@ public List getObject() { if(setModel.getObject()!=null) for (AmpActivitySector ampActivitySector : setModel.getObject()) { - if (ampActivitySector.getClassificationConfig().getId() - .equals(sectorClassification.getId())) + if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) ret.add(ampActivitySector); } - + Comparator comparator = new Comparator(){ @Override public int compare(AmpActivitySector o1, AmpActivitySector o2) { return o1.getSectorId().getSectorPathString().compareTo(o2.getSectorId().getSectorPathString()); } - - + + }; - + Collections.sort(ret, comparator); - + return ret; } }; @@ -110,7 +152,7 @@ public Number getPercentage(AmpActivitySector item) { return item.getSectorPercentage(); } }; - + final Label totalLabel = new Label("totalPercSector", new Model() { @Override public String getObject() { @@ -129,7 +171,7 @@ public String getObject() { }); totalLabel.setOutputMarkupId(true); add(totalLabel); - + percentageValidationField.setIndicatorAppender(iValidator); add(percentageValidationField); @@ -143,14 +185,14 @@ protected void onConfigure() { }; minSizeCollectionValidationField.setIndicatorAppender(iValidator); add(minSizeCollectionValidationField); - - + + final AmpUniqueCollectionValidatorField uniqueCollectionValidationField = new AmpUniqueCollectionValidatorField( "uniqueSectorsValidator", listModel, "uniqueSectorsValidator") { @Override public Object getIdentifier(AmpActivitySector t) { return t.getSectorId().getName(); - } + } }; uniqueCollectionValidationField.setIndicatorAppender(iValidator); add(uniqueCollectionValidationField); @@ -159,11 +201,11 @@ public Object getIdentifier(AmpActivitySector t) { @Override public AmpAutoCompleteDisplayable getItem(AmpActivitySector t) { return t.getSectorId(); - } + } }; treeCollectionValidationField.setIndicatorAppender(iValidator); add(treeCollectionValidationField); - + list = new ListView("listSectors", listModel) { @Override @@ -185,19 +227,18 @@ protected void onAjaxOnUpdate( item.add(percentageField); - item.add(new Label("sectorLabel", item.getModelObject() - .getSectorId().getSectorPathString())); - AmpDeleteLinkField delSector = new AmpDeleteLinkField( - "delSector", "Delete Sector") { + item.add(new Label("sectorLabel", item.getModelObject().getSectorId().getSectorPathString())); + AmpDeleteLinkField delSector = new AmpDeleteLinkField("delSector", "Delete Sector") { @Override public void onClick(AjaxRequestTarget target) { setModel.getObject().remove(item.getModelObject()); + triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(listParent); target.add(totalLabel); list.removeAll(); percentageValidationField.reloadValidationField(target); uniqueCollectionValidationField.reloadValidationField(target); - minSizeCollectionValidationField.reloadValidationField(target); + minSizeCollectionValidationField.reloadValidationField(target); treeCollectionValidationField.reloadValidationField(target); } }; @@ -207,8 +248,9 @@ public void onClick(AjaxRequestTarget target) { }; list.setReuseItems(true); add(list); - - add(new AmpDividePercentageField("dividePercentage", "Divide Percentage", "Divide Percentage", setModel, new Model>(list)){ + + add(new AmpDividePercentageField("dividePercentage", "Divide Percentage", + "Divide Percentage", setModel, new Model>(list)){ private static final long serialVersionUID = 1L; @Override @@ -231,8 +273,8 @@ public boolean itemInCollection(AmpActivitySector item) { } }); - - final AmpAutocompleteFieldPanel searchSectors = new AmpAutocompleteFieldPanel( + + this.searchSectors = new AmpAutocompleteFieldPanel( "searchSectors", "Search " + fmName, AmpSectorSearchModel.class) { private static final long serialVersionUID = 1227775244079125152L; @@ -252,9 +294,9 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { } activitySector.setClassificationConfig(sectorClassification); - if(setModel.getObject() == null) - setModel.setObject(new HashSet()); + if (setModel.getObject() == null) setModel.setObject(new HashSet()); setModel.getObject().add(activitySector); + triggerUpdateEvent(setModel.getObject(), sectorClassification); list.removeAll(); target.add(list.getParent()); percentageValidationField.reloadValidationField(target); @@ -276,13 +318,13 @@ public Integer getChoiceLevel(AmpSector choice) { } }; - searchSectors.getModelParams().put( - AmpSectorSearchModel.PARAM.SECTOR_SCHEME, - sectorClassification.getClassification()); - searchSectors.getModelParams().put( - AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS, 0); - add(searchSectors); + + this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.SECTOR_SCHEME, + sectorClassification.getClassification()); + this.searchSectors.getModelParams().put(AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS, 0); + + add(this.searchSectors); } } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java new file mode 100644 index 00000000000..1046dd38d73 --- /dev/null +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java @@ -0,0 +1,8 @@ +package org.dgfoundation.amp.onepager.interfaces; + +import org.digijava.module.aim.dbentity.AmpSector; +import java.util.List; + +public interface ISectorTableUpdateListener { + void onUpdate(List data); +} diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableUpdateListener.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableUpdateListener.java new file mode 100644 index 00000000000..1046dd38d73 --- /dev/null +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableUpdateListener.java @@ -0,0 +1,8 @@ +package org.dgfoundation.amp.onepager.interfaces; + +import org.digijava.module.aim.dbentity.AmpSector; +import java.util.List; + +public interface ISectorTableUpdateListener { + void onUpdate(List data); +} diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 402385cba52..3150a726e47 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -13,6 +13,7 @@ import org.digijava.kernel.exception.DgException; import org.digijava.kernel.persistence.PersistenceManager; import org.digijava.module.aim.dbentity.AmpSector; +import org.digijava.module.aim.dbentity.AmpSectorMapping; import org.digijava.module.aim.dbentity.AmpSectorScheme; import org.hibernate.Criteria; import org.hibernate.HibernateException; @@ -28,7 +29,8 @@ public class AmpSectorSearchModel extends AbstractAmpAutoCompleteModel { public enum PARAM implements AmpAutoCompleteModelParam { - SECTOR_SCHEME + SECTOR_SCHEME, + SRC_SECTOR_SELECTED // used in case of sector mapping exists }; public AmpSectorSearchModel(String input,String language, @@ -48,27 +50,72 @@ protected Collection load() { session = PersistenceManager.getSession(); session.enableFilter("isDeletedFilter").setParameter("deleted", Boolean.FALSE); - Integer maxResults = (Integer) getParams().get( - AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS); - AmpSectorScheme scheme = (AmpSectorScheme) getParams().get( - PARAM.SECTOR_SCHEME); + Integer maxResults = (Integer) getParams().get(AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS); + AmpSectorScheme scheme = (AmpSectorScheme) getParams().get(PARAM.SECTOR_SCHEME); Criteria crit = session.createCriteria(AmpSector.class); crit.setCacheable(true); - Junction junction = Restrictions.conjunction().add(Restrictions.and(Restrictions.eq("ampSecSchemeId", scheme), Restrictions.or( Restrictions.isNull("deleted"), Restrictions.eq( "deleted", Boolean.FALSE)))); + Junction junction = Restrictions.conjunction().add( + Restrictions.and( + Restrictions.eq("ampSecSchemeId", scheme), + Restrictions.or( + Restrictions.isNull("deleted"), + Restrictions.eq( "deleted", Boolean.FALSE) + ))); + + List srcSectorSelected = (List) getParams().get(PARAM.SRC_SECTOR_SELECTED); + Junction junction2 = null; + if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { + List ids = searchSectorsDstFromMapping(srcSectorSelected); + if (!ids.isEmpty()) { + junction2 = Restrictions.conjunction().add( + Restrictions.in("ampSectorId", ids)); + } + } + crit.add(junction); + if (junction2 != null) crit.add(junction2); crit.addOrder(Order.asc("name")); - if (maxResults != null && maxResults != 0) - crit.setMaxResults(maxResults); + if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); List list = crit.list(); ret = (Collection) createTreeView(list); - } catch (HibernateException e) { throw new RuntimeException(e); } finally { - session.disableFilter("isDeletedFilter"); - } + session.disableFilter("isDeletedFilter"); + } return ret; } + /* + * Search for sectors that are mapped to the given sector + * */ + private List searchSectorsDstFromMapping(List srcSectors) { + List ids = new ArrayList(); + try { + session = PersistenceManager.getSession(); + Criteria crit = session.createCriteria(AmpSectorMapping.class); + crit.setCacheable(true); + + if (srcSectors != null && !srcSectors.isEmpty()) { + Junction junction = Restrictions.conjunction().add( + Restrictions.in("srcSector", srcSectors) + ); + + crit.add(junction); + List list = crit.list(); + if (list != null && !list.isEmpty()) { + for (AmpSectorMapping mapping : list) { + ids.add(mapping.getDstSector().getAmpSectorId()); + } + } + } + } catch (HibernateException e) { + throw new RuntimeException(e); + } finally { + session.disableFilter("isDeletedFilter"); + } + return ids; + } + } diff --git a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java index 928eb69db73..ebb6468e1b5 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/dbentity/AmpActivitySector.java @@ -38,23 +38,6 @@ public class AmpActivitySector implements Versionable, Serializable, Cloneable, private AmpClassificationConfiguration classificationConfig; -// @Interchangeable(fieldTitle = "Indirect Sectors") -// private Set indirectSectors = new HashSet<>(); - - //public Set getIndirectSectors() { return indirectSectors; } - -// public void setIndirectSectors(Set indirectSectors) { -// this.indirectSectors = indirectSectors; -// } -// -// public void addIndirectSector(AmpActivityIndirectSector pIndirectSector) { -// pIndirectSector.setActivitySector(this); -// indirectSectors.add(pIndirectSector); -// } - - - - public AmpClassificationConfiguration getClassificationConfig() { return classificationConfig; } From 2273c263c797b1147d630b958d465bc186106e5c Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Tue, 19 Dec 2023 12:23:18 -0300 Subject: [PATCH 09/85] AMP-30574 - Sector Mappings Deleting secondary sectors mapped in Activity Form if an primary sector was deleted. --- .../AmpSectorsFormSectionFeature.java | 33 ++- .../tables/AmpSectorsFormTableFeature.java | 201 ++++++++++++------ .../ISectorTableDeleteListener.java | 5 +- 3 files changed, 168 insertions(+), 71 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 39bb528e6b7..4a9707aac1d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -11,6 +11,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.request.cycle.RequestCycle; import org.dgfoundation.amp.onepager.components.features.tables.AmpSectorsFormTableFeature; +import org.dgfoundation.amp.onepager.interfaces.ISectorTableDeleteListener; import org.dgfoundation.amp.onepager.util.AmpFMTypes; import org.digijava.module.aim.dbentity.AmpActivityVersion; import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; @@ -24,7 +25,7 @@ * since Oct 20, 2010 */ public class AmpSectorsFormSectionFeature extends AmpFormSectionFeaturePanel - implements ISectorTableUpdateListener { + implements ISectorTableUpdateListener, ISectorTableDeleteListener { private static final long serialVersionUID = -5601918041949098629L; @@ -36,8 +37,7 @@ public class AmpSectorsFormSectionFeature extends AmpFormSectionFeaturePanel * @param fmName * @throws Exception */ - public AmpSectorsFormSectionFeature(String id, String fmName,final IModel am) - throws Exception { + public AmpSectorsFormSectionFeature(String id, String fmName,final IModel am) throws Exception { super(id, fmName, am); this.fmType = AmpFMTypes.MODULE; @@ -60,6 +60,7 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel data) { if (secondarySectorsTable != null) { - // Update user interface if the request is ajax + // Update user interface AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); if (target != null) { secondarySectorsTable.updateBasedOnData(data); @@ -81,4 +88,22 @@ public void onUpdate(List data) { } } + /** + * Updates the user interface when a deletion event is triggered. + * This method is called when the onDelete method is invoked in the containing class. + * + * @param data the AmpSector object to be deleted + */ + @Override + public void onDelete(AmpSector data) { + if (secondarySectorsTable != null) { + // Update user interface + AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); + if (target != null) { + secondarySectorsTable.deleteBasedOnData(data); + secondarySectorsTable.refreshTable(target); + target.add(secondarySectorsTable); + } + } + } } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 84036f76b7f..18bfa3dfcb2 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -21,27 +21,23 @@ import org.dgfoundation.amp.onepager.components.fields.AmpPercentageTextField; import org.dgfoundation.amp.onepager.components.fields.AmpTreeCollectionValidatorField; import org.dgfoundation.amp.onepager.components.fields.AmpUniqueCollectionValidatorField; +import org.dgfoundation.amp.onepager.interfaces.ISectorTableDeleteListener; import org.dgfoundation.amp.onepager.models.AbstractAmpAutoCompleteModel; import org.dgfoundation.amp.onepager.models.AmpSectorSearchModel; import org.dgfoundation.amp.onepager.util.AmpDividePercentageField; import org.dgfoundation.amp.onepager.util.FMUtil; import org.dgfoundation.amp.onepager.yui.AmpAutocompleteFieldPanel; -import org.digijava.module.aim.dbentity.AmpActivitySector; -import org.digijava.module.aim.dbentity.AmpActivityVersion; -import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; -import org.digijava.module.aim.dbentity.AmpSector; +import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.helper.FormatHelper; import org.digijava.module.aim.util.AmpAutoCompleteDisplayable; import org.digijava.module.aim.util.DbUtil; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; + import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; +import org.digijava.module.aim.util.SectorUtil; +import org.hibernate.Session; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.MAXIMUM_PERCENTAGE; @@ -52,16 +48,28 @@ public class AmpSectorsFormTableFeature extends AmpFormTableFeaturePanel { private ISectorTableUpdateListener updateListener; + private ISectorTableDeleteListener deleteListener; public void setUpdateListener(ISectorTableUpdateListener listener) { this.updateListener = listener; } - private AmpAutocompleteFieldPanel searchSectors; - public AmpAutocompleteFieldPanel getSearchSectors() { - return this.searchSectors; + public void setDeleteListener(ISectorTableDeleteListener listener) { + this.deleteListener = listener; } + private AmpAutocompleteFieldPanel searchSectors; + public AmpAutocompleteFieldPanel getSearchSectors() { return this.searchSectors; } + + private IModel> setModel; + public IModel> getSetModel() { return this.setModel; } + + private AmpPercentageCollectionValidatorField percentageValidationField; + private AmpUniqueCollectionValidatorField uniqueCollectionValidationField; + private AmpMinSizeCollectionValidationField minSizeCollectionValidationField; + private AmpTreeCollectionValidatorField treeCollectionValidationField; + + /** * Triggers an update event with the selected sectors based on the given sector classification. * Method called when change the list of selected sectors to update (from the parent component) @@ -94,19 +102,90 @@ public void updateBasedOnData(List selectedSectors) { this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED, selectedSectors); } + protected void triggerDeleteEvent(AmpSector sectorDeleted) { + if (deleteListener != null) { + deleteListener.onDelete(sectorDeleted); + } + } + + /** + * Deletes all sector in the Activity component that are mapped with the given sector. + * + * @param sectorDeleted the sector to delete mappings for + */ + public void deleteBasedOnData(AmpSector sectorDeleted) { + Collection allMappings = SectorUtil.getAllSectorMappings(); + List dstSectorsMappedWithDeletedSector = new ArrayList(); + for (AmpSectorMapping ampSectorMapping : allMappings) { + if (ampSectorMapping.getSrcSector().getAmpSectorId().equals(sectorDeleted.getAmpSectorId())) { + dstSectorsMappedWithDeletedSector.add(ampSectorMapping.getDstSector()); + } + } + + // We search for the destination sectors in Activity + if (!dstSectorsMappedWithDeletedSector.isEmpty()) { + Iterator iterator = setModel.getObject().iterator(); + while (iterator.hasNext()) { + AmpActivitySector sector = iterator.next(); + for (AmpSector dstSectorMapped : dstSectorsMappedWithDeletedSector) { + if (sector.getSectorId().getAmpSectorId().equals(dstSectorMapped.getAmpSectorId())) { + // if the sector is not mapped to another sector + if (dstSectorHasOnlyOneMap(dstSectorMapped, allMappings)) { + iterator.remove(); + } + } + } + } + } + } + + /** + * Checks if a given destination sector has only one mapping in a collection of mappings. + * + * @param dstSector the destination sector to check for + * @param allMappings the collection of all mappings + * @return true if the destination sector has only one mapping, false otherwise + */ + private boolean dstSectorHasOnlyOneMap(AmpSector dstSector, Collection allMappings) { + int count = 0; + for (AmpSectorMapping ampSectorMapping : allMappings) { + if (ampSectorMapping.getDstSector().getAmpSectorId().equals(dstSector.getAmpSectorId())) { + count++; + } + } + return count == 1; + } + + /** + * Clears the table and reloads the validation fields. + * + * @param target the {@link AjaxRequestTarget} used to refresh the table + */ + public void refreshTable(AjaxRequestTarget target) { + list.removeAll(); + percentageValidationField.reloadValidationField(target); + uniqueCollectionValidationField.reloadValidationField(target); + minSizeCollectionValidationField.reloadValidationField(target); + treeCollectionValidationField.reloadValidationField(target); + } + /** - * @param id - * @param fmName - * @param am - * @throws Exception + * Constructor for the AmpSectorsFormTableFeature class. + * + * @param id The id of the component. + * @param fmName The name of the feature model. + * @param am The model for the AmpActivityVersion. + * @param sectorClassification The classification configuration for sectors. + * + * @throws Exception If there is an exception during construction. */ public AmpSectorsFormTableFeature(String id, String fmName, final IModel am, final AmpClassificationConfiguration sectorClassification) throws Exception { super(id, am, fmName, false, true); - final IModel> setModel = new PropertyModel>(am, "sectors"); + setModel = new PropertyModel>(am, "sectors"); if (setModel.getObject() == null) setModel.setObject(new HashSet()); if (sectorClassification.getDescription() != null) addInfoText(sectorClassification.getDescription()); @@ -119,24 +198,19 @@ public List getObject() { // remove sectors with other classification ArrayList ret = new ArrayList(); - if(setModel.getObject()!=null) - for (AmpActivitySector ampActivitySector : setModel.getObject()) { - if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) - ret.add(ampActivitySector); - } - - Comparator comparator = new Comparator(){ - - @Override - public int compare(AmpActivitySector o1, AmpActivitySector o2) { - return o1.getSectorId().getSectorPathString().compareTo(o2.getSectorId().getSectorPathString()); - } - - - }; - - Collections.sort(ret, comparator); + if (setModel.getObject() != null) + for (AmpActivitySector ampActivitySector : setModel.getObject()) { + if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) + ret.add(ampActivitySector); + } + Comparator comparator = new Comparator() { + @Override + public int compare(AmpActivitySector o1, AmpActivitySector o2) { + return o1.getSectorId().getSectorPathString().compareTo(o2.getSectorId().getSectorPathString()); + } + }; + Collections.sort(ret, comparator); return ret; } }; @@ -145,7 +219,7 @@ public int compare(AmpActivitySector o1, AmpActivitySector o2) { add(wmc); AjaxIndicatorAppender iValidator = new AjaxIndicatorAppender(); wmc.add(iValidator); - final AmpPercentageCollectionValidatorField percentageValidationField = new AmpPercentageCollectionValidatorField( + percentageValidationField = new AmpPercentageCollectionValidatorField( "sectorPercentageTotal", listModel, "sectorPercentageTotal") { @Override public Number getPercentage(AmpActivitySector item) { @@ -175,7 +249,7 @@ public String getObject() { percentageValidationField.setIndicatorAppender(iValidator); add(percentageValidationField); - final AmpMinSizeCollectionValidationField minSizeCollectionValidationField = new AmpMinSizeCollectionValidationField( + minSizeCollectionValidationField = new AmpMinSizeCollectionValidationField( "minSizeSectorsValidator", listModel, "minSizeSectorsValidator"){ @Override protected void onConfigure() { @@ -186,8 +260,7 @@ protected void onConfigure() { minSizeCollectionValidationField.setIndicatorAppender(iValidator); add(minSizeCollectionValidationField); - - final AmpUniqueCollectionValidatorField uniqueCollectionValidationField = new AmpUniqueCollectionValidatorField( + uniqueCollectionValidationField = new AmpUniqueCollectionValidatorField( "uniqueSectorsValidator", listModel, "uniqueSectorsValidator") { @Override public Object getIdentifier(AmpActivitySector t) { @@ -196,7 +269,7 @@ public Object getIdentifier(AmpActivitySector t) { }; uniqueCollectionValidationField.setIndicatorAppender(iValidator); add(uniqueCollectionValidationField); - final AmpTreeCollectionValidatorField treeCollectionValidationField = new AmpTreeCollectionValidatorField( + treeCollectionValidationField = new AmpTreeCollectionValidatorField( "treeSectorsValidator", listModel, "treeSectorsValidator") { @Override public AmpAutoCompleteDisplayable getItem(AmpActivitySector t) { @@ -206,13 +279,12 @@ public AmpAutoCompleteDisplayable getItem(AmpActivitySector t) { treeCollectionValidationField.setIndicatorAppender(iValidator); add(treeCollectionValidationField); - list = new ListView("listSectors", listModel) { + @Override protected void populateItem(final ListItem item) { final MarkupContainer listParent = this.getParent(); - PropertyModel percModel = new PropertyModel( - item.getModel(), "sectorPercentage"); + PropertyModel percModel = new PropertyModel(item.getModel(), "sectorPercentage"); AmpPercentageTextField percentageField = new AmpPercentageTextField( "percentage", percModel, "sectorPercentage", @@ -231,19 +303,16 @@ protected void onAjaxOnUpdate( AmpDeleteLinkField delSector = new AmpDeleteLinkField("delSector", "Delete Sector") { @Override public void onClick(AjaxRequestTarget target) { - setModel.getObject().remove(item.getModelObject()); + AmpActivitySector sectorToDelete = item.getModelObject(); + setModel.getObject().remove(sectorToDelete); + triggerDeleteEvent(sectorToDelete.getSectorId()); triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(listParent); target.add(totalLabel); - list.removeAll(); - percentageValidationField.reloadValidationField(target); - uniqueCollectionValidationField.reloadValidationField(target); - minSizeCollectionValidationField.reloadValidationField(target); - treeCollectionValidationField.reloadValidationField(target); + refreshTable(target); } }; item.add(delSector); - } }; list.setReuseItems(true); @@ -265,13 +334,10 @@ public int getPercentage(AmpActivitySector loc) { @Override public boolean itemInCollection(AmpActivitySector item) { - if (item.getClassificationConfig().getId() - .equals(sectorClassification.getId())) + if (item.getClassificationConfig().getId().equals(sectorClassification.getId())) return true; - else - return false; + else return false; } - }); this.searchSectors = new AmpAutocompleteFieldPanel( @@ -297,12 +363,8 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { if (setModel.getObject() == null) setModel.setObject(new HashSet()); setModel.getObject().add(activitySector); triggerUpdateEvent(setModel.getObject(), sectorClassification); - list.removeAll(); target.add(list.getParent()); - percentageValidationField.reloadValidationField(target); - uniqueCollectionValidationField.reloadValidationField(target); - minSizeCollectionValidationField.reloadValidationField(target); - treeCollectionValidationField.reloadValidationField(target); + refreshTable(target); } @Override @@ -314,17 +376,28 @@ public Integer getChoiceLevel(AmpSector choice) { c = c.getParentSectorId(); } return i; - } }; - - this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.SECTOR_SCHEME, sectorClassification.getClassification()); this.searchSectors.getModelParams().put(AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS, 0); + //For mappings between sectors with different classifications, we configure the search to show only the sectors + // mapped to the selected sectors from the primary classification in dropdown list. + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + List primarySectorsSelected = new ArrayList(); + for (AmpActivitySector ampActivitySector : setModel.getObject()) { + if (ampActivitySector.getClassificationConfig().getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { + primarySectorsSelected.add(ampActivitySector.getSectorId()); + } + } + if (!primarySectorsSelected.isEmpty()) { + this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED, + primarySectorsSelected); + } + } + add(this.searchSectors); } - } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java index 1046dd38d73..fb3caf781aa 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/interfaces/ISectorTableDeleteListener.java @@ -1,8 +1,7 @@ package org.dgfoundation.amp.onepager.interfaces; import org.digijava.module.aim.dbentity.AmpSector; -import java.util.List; -public interface ISectorTableUpdateListener { - void onUpdate(List data); +public interface ISectorTableDeleteListener { + void onDelete(AmpSector data); } From 9dc8fcdbd86dd9f6286230fd8939aefe185ef3c2 Mon Sep 17 00:00:00 2001 From: Diego Rossi Date: Thu, 4 Jan 2024 14:08:15 -0300 Subject: [PATCH 10/85] AMP-30574 - Sector Mappings Observations made by Brian --- .../digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java | 1 + amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java index 34b8d1a0a82..d4efd36ab76 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/gpi/ValidationUtils.java @@ -32,6 +32,7 @@ public static void requireValid(Object obj) { } public static void valuesValid(Collection possibleValues, Object value) { + if (possibleValues == null) { throw new IllegalArgumentException("possibleValues cannot be null"); } for (Object possibleValue : possibleValues) { if (possibleValue.equals(value)) { return; diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index 6a18ba6d350..fc882019a37 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -1262,7 +1262,6 @@ public static Collection getAllSectorMappings() { queryString = "select asm from " + AmpSectorMapping.class.getName() + " asm"; qry = session.createQuery(queryString); col = qry.list(); - session.flush(); } catch (Exception ex) { logger.error("Unable to get sectors mappings from database " + ex.getMessage()); ex.printStackTrace(System.out); From e3ea6d6c890d4618adf78a7742f6a41de7a87483 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 09:24:45 +0300 Subject: [PATCH 11/85] AMP-30574 Map primary and secondary sectors --- amp/repository/aim/components.xml | 21 +++++++++++---------- amp/repository/aim/view/adminHome.jsp | 27 +++++++++++++++++++++------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/amp/repository/aim/components.xml b/amp/repository/aim/components.xml index 49f1bb93a0f..ca80aec461a 100644 --- a/amp/repository/aim/components.xml +++ b/amp/repository/aim/components.xml @@ -1,7 +1,7 @@ - + @@ -9,7 +9,7 @@ - + @@ -29,6 +29,7 @@ + @@ -176,7 +177,7 @@ - + @@ -258,7 +259,7 @@ - + @@ -319,11 +320,11 @@ - + - + - + @@ -392,7 +393,7 @@ - + @@ -418,7 +419,7 @@ myReports.jsp - + myLastVersions.jsp @@ -538,7 +539,7 @@ viewChannelOverviewObjective.jsp - + viewRegionalObservations.jsp diff --git a/amp/repository/aim/view/adminHome.jsp b/amp/repository/aim/view/adminHome.jsp index 622513674b4..303e3afe679 100644 --- a/amp/repository/aim/view/adminHome.jsp +++ b/amp/repository/aim/view/adminHome.jsp @@ -112,15 +112,15 @@ Click here to access Data Freeze Manager - + Data Freeze Manager - - + + @@ -128,7 +128,7 @@ Click here to access Project Performance Alerts Manager - Project Performance Alerts Manager @@ -217,6 +217,21 @@ + + + + + + + Click here to view Sector Mapping + + + Sector Mapping + + + + + @@ -343,7 +358,7 @@ GIS Layers Manager - + @@ -694,7 +709,7 @@ Click here to view Document manager admin - + Resource Manager From 1a27bb7790cbe984e864fe28fdd17969e346d13f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 09:51:14 +0300 Subject: [PATCH 12/85] AMP-30574 Map primary and secondary sectors --- amp/repository/aim/view/allVisibilityTags.jsp | 1098 ++++++++-------- .../aim/view/allVisibilityTagsComputed.jsp | 1133 +++++++++-------- 2 files changed, 1117 insertions(+), 1114 deletions(-) diff --git a/amp/repository/aim/view/allVisibilityTags.jsp b/amp/repository/aim/view/allVisibilityTags.jsp index 7bf0ec33a5d..1f8c97e0117 100644 --- a/amp/repository/aim/view/allVisibilityTags.jsp +++ b/amp/repository/aim/view/allVisibilityTags.jsp @@ -12,29 +12,31 @@ <%@ taglib uri="/taglib/moduleVisibility" prefix="module" %> <%@ taglib uri="/taglib/jstl-functions" prefix="fn" %> - - + + - - - - - - - - - + + + + + + + + + + + - - + + @@ -58,32 +60,32 @@ - - + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - + + + @@ -93,8 +95,8 @@ - - + + @@ -120,7 +122,7 @@ - + @@ -129,7 +131,7 @@ - + @@ -144,33 +146,33 @@ - + - - - - - - - + + + + + + + - - + + - - - - + + + + - - - - + + + + - - + + @@ -182,93 +184,93 @@ - - - - - - - - - + + + + + + + + + - - + + - + - - - - - - - - + + + + + + + + - - + + - - + + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - - + + + - - - - - + + + + + - - - - - - - + + + + + + + - + - + @@ -293,7 +295,7 @@ - + @@ -307,9 +309,9 @@ - - - + + + @@ -333,52 +335,52 @@ - - - + + + - + - - - - - + + + + + - - + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + @@ -386,7 +388,7 @@ - + @@ -394,102 +396,102 @@ - - - - - - - + + + + + + + - - + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + @@ -502,7 +504,7 @@ -<%-- +<%-- deleted 6 fields, do not reinsert --%> @@ -534,146 +536,146 @@ deleted 6 fields, do not reinsert - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - + - + - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - + - - + + - - + + @@ -681,7 +683,7 @@ deleted 6 fields, do not reinsert - + @@ -696,51 +698,51 @@ deleted 6 fields, do not reinsert - - - - - - - - - - - + + + + + + + + + + + - - - + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + @@ -751,91 +753,91 @@ deleted 6 fields, do not reinsert - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - + - - + + @@ -844,31 +846,31 @@ deleted 6 fields, do not reinsert - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + + - - - - - - + + + + + + @@ -890,30 +892,30 @@ deleted 6 fields, do not reinsert - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -921,46 +923,46 @@ deleted 6 fields, do not reinsert - - - - - - + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -982,8 +984,8 @@ deleted 6 fields, do not reinsert - - + + @@ -993,7 +995,7 @@ deleted 6 fields, do not reinsert - + @@ -1006,11 +1008,11 @@ deleted 6 fields, do not reinsert - - + + - - + + @@ -1116,10 +1118,10 @@ deleted 6 fields, do not reinsert - - - - + + + + diff --git a/amp/repository/aim/view/allVisibilityTagsComputed.jsp b/amp/repository/aim/view/allVisibilityTagsComputed.jsp index 4930728c687..68dd2a704dd 100644 --- a/amp/repository/aim/view/allVisibilityTagsComputed.jsp +++ b/amp/repository/aim/view/allVisibilityTagsComputed.jsp @@ -11,25 +11,26 @@ <%@ taglib uri="/taglib/featureVisibility" prefix="feature" %> <%@ taglib uri="/taglib/moduleVisibility" prefix="module" %> <%@ taglib uri="/taglib/jstl-functions" prefix="fn" %> - + - + + - + - - - - - - - - + + + + + + + + @@ -52,38 +53,38 @@ - - - - - - - - - - + + + + + + + + + + - - - - - - + + + + + + - - - + + + - - - + + + @@ -97,19 +98,19 @@ - - - - - - - - - - - + + + + + + + + + + + - + @@ -117,586 +118,586 @@ - + - - + + - - - - + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + - - + + - - - - - + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - + + + + + + + + + - + @@ -772,27 +773,27 @@ - - + + - + - + - + - - + + - - + + @@ -819,9 +820,9 @@ - - + + - \ No newline at end of file + From 2acd15d9abe2c2009746b74a82028c5b47c0bcd4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 10:05:19 +0300 Subject: [PATCH 13/85] AMP-30574 Map primary and secondary sectors --- amp/repository/aim/view/adminHome.jsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/repository/aim/view/adminHome.jsp b/amp/repository/aim/view/adminHome.jsp index 3018cb03e5e..c1060e891eb 100644 --- a/amp/repository/aim/view/adminHome.jsp +++ b/amp/repository/aim/view/adminHome.jsp @@ -238,9 +238,9 @@ Click here to view Sector Mapping - + Sector Mapping - + From 30a15af96923cec3da91ff728be65f3d3682a0df Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 10:25:33 +0300 Subject: [PATCH 14/85] AMP-30574 Map primary and secondary sectors --- .../src/modules/admin/sectorMapping/components/Startup.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx index 131ac5ba65d..010e617268b 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/components/Startup.jsx @@ -21,7 +21,7 @@ class Startup extends Component { if (translationPending) { return (); } else { - document.title = translations['amp.admin.ndd:page-title']; + document.title = translations['amp.admin.sectorMapping:page-title']; return ( {children} From 6733669a8737ad437b331fbfdaed6771accf3d8d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 10:54:22 +0300 Subject: [PATCH 15/85] AMP-30574 Map primary and secondary sectors --- .../sector/config/initialTranslations.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json index f5b9e5b9403..03594835086 100644 --- a/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json +++ b/amp/TEMPLATE/reampv2/src/modules/admin/sectorMapping/sector/config/initialTranslations.json @@ -1,5 +1,6 @@ { "amp.admin.sectorMapping:title": "Sectors Mapping", + "amp.admin.sectorMapping:page-title": "Aid Management Platform - Sector Mapping Manager", "amp.admin.sectorMapping:tooltip-direct-sector": "The main sector in use. What is selected in the Activity Form for the Source Sector will limit what options are available in the Destination Sector drop down.", "amp.admin.sectorMapping:tooltip-indirect-sector": "The indirect sector is mapped against your reference sector. If your reference sector is your national development strategy, the indirect sector could be any regional/international strategy (ex. SDGs, New Deal, etc.) or vice-versa. Please note that the indirect sector will be displayed in the inner ring of the dashboard. The assigned percentage will be automatically distributed. This sector will not be seen in the activity form.", "amp.admin.sectorMapping:choose_main_src_sector": "Choose a Source Sector", @@ -10,6 +11,7 @@ "amp.admin.sectorMapping:dst-sector": "Destination Sector", "amp.admin.sectorMapping:src-sectors": "Source Sectors", "amp.admin.sectorMapping:dst-sectors": "Destination Sectors", + "amp.admin.sectorMapping:required-fields": "Required Fields", "amp.admin.sectorMapping:warning_on_change_main_sector": "If you change the Source Sector or the Destination Sector the existing mappings will be deleted, are you sure you want to continue?", "amp.admin.sectorMapping:notification-saved-ok": "All mappings saved correctly.", "amp.admin.sectorMapping:add-new": "Add New", @@ -30,5 +32,12 @@ "amp.admin.sectorMapping:choose_src_scheme": "Choose a Source Scheme", "amp.admin.sectorMapping:choose_dst_scheme": "Choose a Destination Scheme", "amp.admin.sectorMapping:choose_src_sector": "Choose a Source Sector", - "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector" + "amp.admin.sectorMapping:choose_dst_sector": "Choose a Destination Sector", + "amp.admin.sectorMapping:menu-item-sector-mappings": "Sector Mappings", + "amp.admin.sectorMapping:mappings-manager": "Sector Mapping Manager", + "amp.admin.sectorMapping:sub-title": "This module manages the mappings between primary and secondary sectors.", + "amp.admin.sectorMapping:loading": "Loading...", + "amp.admin.sectorMapping:actions": "Actions", + "amp.admin.sectorMapping:click-save": "Click the Save icon to save the added data row", + "amp.admin.sectorMapping:no-matches-found": "No matches found" } From ccdb1548049864aef0511f4dea77983add9e023b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 11:50:28 +0300 Subject: [PATCH 16/85] AMP-30574 Map primary and secondary sectors --- .../sectorMapping/SectorMappingService.java | 11 ++++-- .../dto/GenericSelectObjDTO.java | 3 +- .../digijava/module/aim/util/SectorUtil.java | 39 ++++++++----------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index dda100afa58..126d0621eff 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -7,6 +7,7 @@ import org.digijava.kernel.ampapi.endpoints.sectorMapping.dto.SchemaClassificationDTO; import org.digijava.kernel.exception.DgException; import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.helper.Sector; import org.digijava.module.aim.util.SectorUtil; import java.util.*; import java.util.stream.Collectors; @@ -23,7 +24,11 @@ public List getSectorsByScheme(final Long schemeId) { List sectors = new ArrayList<>(); SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { - sectors.add(new GenericSelectObjDTO(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName())); + GenericSelectObjDTO objDTO =new GenericSelectObjDTO(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName()); + + Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); + childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); + sectors.add(objDTO); }); return sectors; @@ -58,8 +63,8 @@ public MappingConfigurationDTO getMappingsConf() { // All Mappings mappingConf.sectorMapping = SectorUtil.getAllSectorMappings(); if (mappingConf.sectorMapping != null && !mappingConf.sectorMapping.isEmpty()) { - AmpSector srcSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getSrcSector(); - AmpSector dstSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getDstSector(); + AmpSector srcSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getSrcSector(); + AmpSector dstSector = ((AmpSectorMapping) ((ArrayList)mappingConf.sectorMapping).get(0)).getDstSector(); Long srcSchemeId = srcSector.getAmpSecSchemeId().getAmpSecSchemeId(); if (srcSchemeId != null) { diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java index b7962f3c6e4..fbed211a186 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java @@ -1,10 +1,11 @@ package org.digijava.kernel.ampapi.endpoints.sectorMapping.dto; +import java.util.ArrayList; import java.util.List; public class GenericSelectObjDTO { public Long id; public String value; - public List children; + public List children=new ArrayList<>(); public GenericSelectObjDTO(Long id, String value) { this.id = id; diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index fc882019a37..9413b6417a8 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -289,8 +289,8 @@ public static void updateSubSectors(AmpSector sector, } } - for (int i = 0; i < sectorList.size(); i++) { - Sector sec = (Sector) sectorList.get(i); + for (Object o : sectorList) { + Sector sec = (Sector) o; updateSectorOrganisation(sec.getSectorId(), organisation); } } catch (Exception e) { @@ -300,17 +300,17 @@ public static void updateSubSectors(AmpSector sector, } // Retreives all sub-sectors within the sector with id 'parentSecId' - public static Collection getSubSectors(Long parentSecId) { + public static Collection getSubSectors(Long parentSecId) { - Session session = null; - Query qry = null; - Collection col = new ArrayList(); - Iterator itr = null; - AmpSector ampSector = null; + Session session; + Query qry; + Collection col = new ArrayList<>(); + Iterator itr; + AmpSector ampSector; try { session = PersistenceManager.getSession(); - String queryString = new String(); + String queryString; if (parentSecId.intValue() == 0) { queryString = "select s from " + AmpSector.class.getName() @@ -323,6 +323,7 @@ public static Collection getSubSectors(Long parentSecId) { + " and (s.deleted is null or s.deleted = false) order by " + AmpSector.hqlStringForName("s"); qry = session.createQuery(queryString); + qry.setCacheable(true); qry.setParameter("parentSectorId", parentSecId, LongType.INSTANCE); } itr = qry.list().iterator(); @@ -557,29 +558,21 @@ public static List getAmpSectorsAndSubSectors( Long id = null; Collection configs = SectorUtil .getAllClassificationConfigs(); - Iterator confIter = configs - .iterator(); - while (confIter.hasNext()) { - AmpClassificationConfiguration conf = confIter.next(); - if (configurationName.equals(conf.getName())) { - if (conf.getClassification() != null) - id = conf.getClassification().getAmpSecSchemeId(); - } + for (AmpClassificationConfiguration conf : configs) { + if (configurationName.equals(conf.getName()) && (conf.getClassification() != null)) + {id = conf.getClassification().getAmpSecSchemeId(); } + } if (id != null) { Collection dbReturnSet = SectorUtil .getAllParentSectors(id); - Iterator iter = dbReturnSet.iterator(); - while (iter.hasNext()) { - AmpSector ampSector = iter.next(); + for (AmpSector ampSector : dbReturnSet) { ampSector.setName(ampSector.getName().toUpperCase()); ret.add(ampSector); Collection dbChildReturnSet = SectorUtil .getAllChildSectors(ampSector.getAmpSectorId()); - Iterator iterSub = dbChildReturnSet.iterator(); - while (iterSub.hasNext()) { - AmpSector ampSubSector = (AmpSector) iterSub.next(); + for (AmpSector ampSubSector : dbChildReturnSet) { String temp = " -- " + ampSubSector.getName(); ampSubSector.setName(temp); ret.add(ampSubSector); From d687a16b0d32006d316c1d6e79009400894fa442 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:03:02 +0300 Subject: [PATCH 17/85] AMP-30574 Map primary and secondary sectors --- .../sectorMapping/SectorMappingService.java | 11 +++++---- .../dto/GenericSelectObjDTO.java | 2 ++ .../digijava/module/aim/util/SectorUtil.java | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index 126d0621eff..b95baec132a 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -23,11 +23,12 @@ public class SectorMappingService { public List getSectorsByScheme(final Long schemeId) { List sectors = new ArrayList<>(); - SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { - GenericSelectObjDTO objDTO =new GenericSelectObjDTO(((AmpSector) sector).getAmpSectorId(), ((AmpSector) sector).getName()); - - Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); - childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); + SectorUtil.getSectorsByScheme(schemeId.intValue()).forEach(sector -> { + AmpSector sec = (AmpSector) sector; + GenericSelectObjDTO objDTO =new GenericSelectObjDTO(sec.getAmpSectorId(), sec.getName()); + objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(),sec.getParentSectorId().getName()); +// Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); +// childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); sectors.add(objDTO); }); diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java index fbed211a186..39604db221c 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java @@ -6,6 +6,8 @@ public class GenericSelectObjDTO { public Long id; public String value; public List children=new ArrayList<>(); + public GenericSelectObjDTO parent; + public GenericSelectObjDTO(Long id, String value) { this.id = id; diff --git a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java index 9413b6417a8..e16b057b297 100644 --- a/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java +++ b/amp/WEB-INF/src/org/digijava/module/aim/util/SectorUtil.java @@ -730,6 +730,30 @@ public static Collection getSectorLevel1(Integer schemeId) { return col; } + + public static Collection getSectorsByScheme(Integer schemeId) { + String queryString = null; + Session session = null; + Collection col = null; + Query qry = null; + + try { + session = PersistenceManager.getSession(); + queryString = "select pi from " + + AmpSector.class.getName() + + " pi where pi.ampSecSchemeId=:schemeId and (pi.deleted is null or pi.deleted = false) order by " + AmpSector.hqlStringForName("pi"); + qry = session.createQuery(queryString); + qry.setParameter("schemeId", schemeId, IntegerType.INSTANCE); + col = qry.list(); + // session.flush(); + } catch (Exception ex) { + logger.error("Unable to get report names from database " + + ex.getMessage()); + ex.printStackTrace(System.out); + } + return col; + } + /* * get scheme to be edited */ From 869f3aa7fcba10e10d9fcdac689b18f9324c3a4f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:11:01 +0300 Subject: [PATCH 18/85] AMP-30574 Map primary and secondary sectors --- .../ampapi/endpoints/sectorMapping/SectorMappingService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index b95baec132a..07125776c68 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -26,7 +26,9 @@ public List getSectorsByScheme(final Long schemeId) { SectorUtil.getSectorsByScheme(schemeId.intValue()).forEach(sector -> { AmpSector sec = (AmpSector) sector; GenericSelectObjDTO objDTO =new GenericSelectObjDTO(sec.getAmpSectorId(), sec.getName()); - objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(),sec.getParentSectorId().getName()); + if (sec.getParentSectorId()!=null) { + objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(), sec.getParentSectorId().getName()); + } // Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); // childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); sectors.add(objDTO); From eeb7d690f193dceed2579a19cf070c41a88e8d99 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:29:13 +0300 Subject: [PATCH 19/85] AMP-30574 Map primary and secondary sectors --- .../endpoints/sectorMapping/SectorMappingService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index 07125776c68..f83c7b12f4c 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -26,11 +26,11 @@ public List getSectorsByScheme(final Long schemeId) { SectorUtil.getSectorsByScheme(schemeId.intValue()).forEach(sector -> { AmpSector sec = (AmpSector) sector; GenericSelectObjDTO objDTO =new GenericSelectObjDTO(sec.getAmpSectorId(), sec.getName()); - if (sec.getParentSectorId()!=null) { - objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(), sec.getParentSectorId().getName()); - } -// Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); -// childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); +// if (sec.getParentSectorId()!=null) { +// objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(), sec.getParentSectorId().getName()); +// } + Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); + childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); sectors.add(objDTO); }); From 1ba157cb3a9d2b962242248864f6fc6de9cbd8a6 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:29:39 +0300 Subject: [PATCH 20/85] AMP-30574 Map primary and secondary sectors --- .../ampapi/endpoints/sectorMapping/SectorMappingService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index f83c7b12f4c..6791d0caa00 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -23,7 +23,7 @@ public class SectorMappingService { public List getSectorsByScheme(final Long schemeId) { List sectors = new ArrayList<>(); - SectorUtil.getSectorsByScheme(schemeId.intValue()).forEach(sector -> { + SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { AmpSector sec = (AmpSector) sector; GenericSelectObjDTO objDTO =new GenericSelectObjDTO(sec.getAmpSectorId(), sec.getName()); // if (sec.getParentSectorId()!=null) { From 691053d948ccba2f221cd17f55c21dcf90f17c16 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:30:29 +0300 Subject: [PATCH 21/85] AMP-30574 Map primary and secondary sectors --- .../ampapi/endpoints/sectorMapping/SectorMappingService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index 6791d0caa00..b5080fe2299 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -29,7 +29,7 @@ public List getSectorsByScheme(final Long schemeId) { // if (sec.getParentSectorId()!=null) { // objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(), sec.getParentSectorId().getName()); // } - Collection childSectors = SectorUtil.getSubSectors(((AmpSector) sector).getAmpSectorId()); + Collection childSectors = SectorUtil.getSubSectors(sec.getAmpSectorId()); childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); sectors.add(objDTO); }); From b09bbceb22b4e78fb2e9a9475a85a9759483ff88 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 12:30:51 +0300 Subject: [PATCH 22/85] AMP-30574 Map primary and secondary sectors --- .../ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java | 1 - 1 file changed, 1 deletion(-) diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java index 39604db221c..f90ee24924a 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/dto/GenericSelectObjDTO.java @@ -6,7 +6,6 @@ public class GenericSelectObjDTO { public Long id; public String value; public List children=new ArrayList<>(); - public GenericSelectObjDTO parent; public GenericSelectObjDTO(Long id, String value) { From 3f2849896592074a5cb3ef046d5dcef9f1daa40c Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 14:14:02 +0300 Subject: [PATCH 23/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 27 +++++++++++++++++++ .../onepager/models/AmpSectorSearchModel.java | 6 +++-- .../sectorMapping/SectorMappingService.java | 9 +++---- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 18bfa3dfcb2..4e6dabf2b3d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -365,6 +365,33 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(list.getParent()); refreshTable(target); + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + this.getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + activitySector); + } + } + + + @Override + public Collection getChoices(String input) { + Collection choices = super.getChoices(input); + logger.info("getChoices: "+ choices); + + // Get the selected sectors from the parent component + AmpActivitySector selectedSector = (AmpActivitySector) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); + logger.info("Selected sectors: " + selectedSector); + // Remove the already selected sectors from the choices list + for (AmpSector sector : choices) { + + if (Objects.equals(sector.getAmpSectorId(), selectedSector.getSectorId().getAmpSectorId())) + { + logger.info("Removing sector: " + sector.getAmpSectorId()); + choices.remove(sector); + break; + } + } + + return choices; } @Override diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 3150a726e47..127f31bac0d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -30,8 +30,10 @@ public class AmpSectorSearchModel extends public enum PARAM implements AmpAutoCompleteModelParam { SECTOR_SCHEME, - SRC_SECTOR_SELECTED // used in case of sector mapping exists - }; + SRC_SECTOR_SELECTED, // used in case of sector mapping exists + DST_SECTOR_SELECTED, + DST_SECTORS_FOUND + } public AmpSectorSearchModel(String input,String language, Map params) { diff --git a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java index b5080fe2299..fa2fac2d909 100644 --- a/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java +++ b/amp/WEB-INF/src/org/digijava/kernel/ampapi/endpoints/sectorMapping/SectorMappingService.java @@ -26,11 +26,10 @@ public List getSectorsByScheme(final Long schemeId) { SectorUtil.getSectorLevel1(schemeId.intValue()).forEach(sector -> { AmpSector sec = (AmpSector) sector; GenericSelectObjDTO objDTO =new GenericSelectObjDTO(sec.getAmpSectorId(), sec.getName()); -// if (sec.getParentSectorId()!=null) { -// objDTO.parent = new GenericSelectObjDTO(sec.getParentSectorId().getAmpSectorId(), sec.getParentSectorId().getName()); -// } - Collection childSectors = SectorUtil.getSubSectors(sec.getAmpSectorId()); - childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getSectorId(), s.getSectorName()))); + Set childSectors = sec.getSectors(); + if (childSectors!=null) { + childSectors.forEach(s -> objDTO.children.add(new GenericSelectObjDTO(s.getAmpSectorId(), s.getName()))); + } sectors.add(objDTO); }); From e984e4390c31ee495c24f4ab2a16cc631037932a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 14:23:56 +0300 Subject: [PATCH 24/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 4e6dabf2b3d..9574fc8295b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -374,20 +374,26 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { @Override public Collection getChoices(String input) { + Collection choices = super.getChoices(input); + logger.info("getChoices: "+ choices); + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - // Get the selected sectors from the parent component - AmpActivitySector selectedSector = (AmpActivitySector) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); - logger.info("Selected sectors: " + selectedSector); - // Remove the already selected sectors from the choices list - for (AmpSector sector : choices) { - - if (Objects.equals(sector.getAmpSectorId(), selectedSector.getSectorId().getAmpSectorId())) - { - logger.info("Removing sector: " + sector.getAmpSectorId()); - choices.remove(sector); - break; + // Get the selected sectors from the parent component + + AmpActivitySector selectedSector = (AmpActivitySector) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); + logger.info("Selected sectors: " + selectedSector); + // Remove the already selected sectors from the choices list + if(selectedSector!=null) { + for (AmpSector sector : choices) { + + if (Objects.equals(sector.getAmpSectorId(), selectedSector.getSectorId().getAmpSectorId())) { + logger.info("Removing sector: " + sector.getAmpSectorId()); + choices.remove(sector); + break; + } + } } } From 7d434748588ba7f54b9cd2a954cba21a6990f1f8 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 14:35:49 +0300 Subject: [PATCH 25/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 9574fc8295b..d8263f4b9ea 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -34,6 +34,7 @@ import java.text.DecimalFormat; import java.util.*; +import java.util.stream.Collectors; import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; import org.digijava.module.aim.util.SectorUtil; @@ -377,24 +378,16 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); - logger.info("getChoices: "+ choices); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + Collection selectedSectors = setModel.getObject(); + logger.info("Selected sectors: " + selectedSectors); - // Get the selected sectors from the parent component - - AmpActivitySector selectedSector = (AmpActivitySector) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); - logger.info("Selected sectors: " + selectedSector); - // Remove the already selected sectors from the choices list - if(selectedSector!=null) { - for (AmpSector sector : choices) { - - if (Objects.equals(sector.getAmpSectorId(), selectedSector.getSectorId().getAmpSectorId())) { - logger.info("Removing sector: " + sector.getAmpSectorId()); - choices.remove(sector); - break; - } - } + if (selectedSectors!= null) { + choices.removeAll(selectedSectors.stream() + .map(AmpActivitySector::getSectorId) + .collect(Collectors.toList())); } + } return choices; From 565d8fc99892b701602a860e1b6f15f9c52345fa Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 14:40:58 +0300 Subject: [PATCH 26/85] AMP-30574 Map primary and secondary sectors --- .../features/tables/AmpSectorsFormTableFeature.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index d8263f4b9ea..35edb1ebd48 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -367,8 +367,10 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { target.add(list.getParent()); refreshTable(target); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - this.getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, - activitySector); + if (this.getChoices("").size() == 1) { + AmpSector onlyChoice = this.getChoices("").iterator().next(); + onSelect(target, onlyChoice); + } } } From 1660a9cdafde69a82600d66c8cdb7f66d4da9fa4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 14:59:11 +0300 Subject: [PATCH 27/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 35edb1ebd48..41e35ea28f6 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -341,6 +341,9 @@ public boolean itemInCollection(AmpActivitySector item) { } }); + this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + new ArrayList<>()); + this.searchSectors = new AmpAutocompleteFieldPanel( "searchSectors", "Search " + fmName, AmpSectorSearchModel.class) { private static final long serialVersionUID = 1227775244079125152L; @@ -367,10 +370,13 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { target.add(list.getParent()); refreshTable(target); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - if (this.getChoices("").size() == 1) { - AmpSector onlyChoice = this.getChoices("").iterator().next(); - onSelect(target, onlyChoice); - } +// if (this.getChoices("").size() == 1) { +// AmpSector onlyChoice = this.getChoices("").iterator().next(); +// onSelect(target, onlyChoice); +// } + this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); + ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); } } @@ -381,7 +387,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - Collection selectedSectors = setModel.getObject(); + List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); logger.info("Selected sectors: " + selectedSectors); if (selectedSectors!= null) { From 6fc336746cac6dcff7b0df6fe5f8e1640028a69d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 15:09:02 +0300 Subject: [PATCH 28/85] AMP-30574 Map primary and secondary sectors --- .../features/tables/AmpSectorsFormTableFeature.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 41e35ea28f6..b2c4f2f2028 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -341,8 +341,6 @@ public boolean itemInCollection(AmpActivitySector item) { } }); - this.searchSectors.getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, - new ArrayList<>()); this.searchSectors = new AmpAutocompleteFieldPanel( "searchSectors", "Search " + fmName, AmpSectorSearchModel.class) { @@ -377,6 +375,10 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, k -> new ArrayList<>()); ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); + if (this.getChoices("").size() == 1) + { + this.getModel().setObject(((List)this.getChoices("")).get(0)); + } } } From 753aa13eb4cc110a31a504c736f7877bbeee634d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 15:17:18 +0300 Subject: [PATCH 29/85] AMP-30574 Map primary and secondary sectors --- .../features/tables/AmpSectorsFormTableFeature.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index b2c4f2f2028..1d643297e2f 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -375,10 +375,10 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, k -> new ArrayList<>()); ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); - if (this.getChoices("").size() == 1) - { - this.getModel().setObject(((List)this.getChoices("")).get(0)); - } +// if (this.getChoices("").size() == 1) +// { +// this.getModel().setObject(((List)this.getChoices("")).get(0)); +// } } } From b78696805554f20e8c7b43149280e1a4da386868 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 15:29:08 +0300 Subject: [PATCH 30/85] AMP-30574 Map primary and secondary sectors --- .../features/tables/AmpSectorsFormTableFeature.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 1d643297e2f..b6b493ab6d8 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -393,9 +393,16 @@ public Collection getChoices(String input) { logger.info("Selected sectors: " + selectedSectors); if (selectedSectors!= null) { - choices.removeAll(selectedSectors.stream() - .map(AmpActivitySector::getSectorId) - .collect(Collectors.toList())); + for (AmpSector choice : choices) { + for (AmpActivitySector ampActivitySector: selectedSectors) + { + if (Objects.equals(choice.getAmpSectorId(), ampActivitySector.getSectorId().getAmpSectorId())) + { + choices.remove(choice); + break; + } + } + } } } From 52c91ba9c6f02496c5527c1ccf0cb8635cbcea78 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 15:33:12 +0300 Subject: [PATCH 31/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 1 + 1 file changed, 1 insertion(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index b6b493ab6d8..6b27eb44185 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -306,6 +306,7 @@ protected void onAjaxOnUpdate( public void onClick(AjaxRequestTarget target) { AmpActivitySector sectorToDelete = item.getModelObject(); setModel.getObject().remove(sectorToDelete); + ((List)getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).remove(sectorToDelete); triggerDeleteEvent(sectorToDelete.getSectorId()); triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(listParent); From fe3874040b896d6ccc41947293cef1bfda726e6f Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 15:48:34 +0300 Subject: [PATCH 32/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 6b27eb44185..4aacc5cf94d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -388,13 +388,14 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { public Collection getChoices(String input) { Collection choices = super.getChoices(input); + Set choices2 = new HashSet<>(choices); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); logger.info("Selected sectors: " + selectedSectors); if (selectedSectors!= null) { - for (AmpSector choice : choices) { + for (AmpSector choice : choices2) { for (AmpActivitySector ampActivitySector: selectedSectors) { if (Objects.equals(choice.getAmpSectorId(), ampActivitySector.getSectorId().getAmpSectorId())) From 581b1740d2e5b3f3b3abc26e64d44a0a5407c731 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 16:05:23 +0300 Subject: [PATCH 33/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 4aacc5cf94d..165ee6ed1b2 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -39,6 +39,7 @@ import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; import org.digijava.module.aim.util.SectorUtil; import org.hibernate.Session; +import org.jetbrains.annotations.NotNull; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.MAXIMUM_PERCENTAGE; @@ -352,8 +353,8 @@ protected String getChoiceValue(AmpSector choice) { return DbUtil.filter(choice.getName()); } - @Override - public void onSelect(AjaxRequestTarget target, AmpSector choice) { + + private AmpActivitySector getAmpActivitySector(AmpSector choice) { AmpActivitySector activitySector = new AmpActivitySector(); activitySector.setSectorId(choice); activitySector.setActivityId(am.getObject()); @@ -363,6 +364,12 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { } activitySector.setClassificationConfig(sectorClassification); + return activitySector; + } + + @Override + public void onSelect(AjaxRequestTarget target, AmpSector choice) { + AmpActivitySector activitySector = getAmpActivitySector(choice); if (setModel.getObject() == null) setModel.setObject(new HashSet()); setModel.getObject().add(activitySector); triggerUpdateEvent(setModel.getObject(), sectorClassification); @@ -376,10 +383,11 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, k -> new ArrayList<>()); ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); -// if (this.getChoices("").size() == 1) -// { -// this.getModel().setObject(((List)this.getChoices("")).get(0)); -// } + if (this.getChoices("").size() == 1) + { + activitySector = getAmpActivitySector(((List)this.getChoices("")).get(0)); + setModel.getObject().add(activitySector); + } } } @@ -445,4 +453,6 @@ public Integer getChoiceLevel(AmpSector choice) { add(this.searchSectors); } + + } From b758f905363032bf42ec1c5fc0989e166757f380 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 16:22:38 +0300 Subject: [PATCH 34/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 165ee6ed1b2..11a7edef299 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -307,7 +307,10 @@ protected void onAjaxOnUpdate( public void onClick(AjaxRequestTarget target) { AmpActivitySector sectorToDelete = item.getModelObject(); setModel.getObject().remove(sectorToDelete); - ((List)getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).remove(sectorToDelete); + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME) && (getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)!=null)) { + ((List) getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).remove(sectorToDelete); + + } triggerDeleteEvent(sectorToDelete.getSectorId()); triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(listParent); @@ -370,23 +373,23 @@ private AmpActivitySector getAmpActivitySector(AmpSector choice) { @Override public void onSelect(AjaxRequestTarget target, AmpSector choice) { AmpActivitySector activitySector = getAmpActivitySector(choice); - if (setModel.getObject() == null) setModel.setObject(new HashSet()); + if (setModel.getObject() == null) setModel.setObject(new HashSet<>()); setModel.getObject().add(activitySector); triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(list.getParent()); refreshTable(target); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { -// if (this.getChoices("").size() == 1) { -// AmpSector onlyChoice = this.getChoices("").iterator().next(); -// onSelect(target, onlyChoice); -// } this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, k -> new ArrayList<>()); ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); + logger.info("Choices: " + this.getChoices("")); if (this.getChoices("").size() == 1) { activitySector = getAmpActivitySector(((List)this.getChoices("")).get(0)); setModel.getObject().add(activitySector); + triggerUpdateEvent(setModel.getObject(), sectorClassification); + target.add(list.getParent()); + refreshTable(target); } } } @@ -400,7 +403,6 @@ public Collection getChoices(String input) { if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); - logger.info("Selected sectors: " + selectedSectors); if (selectedSectors!= null) { for (AmpSector choice : choices2) { From 49f5a857ed437d6871f908582df36428b8d19ddf Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 16:44:44 +0300 Subject: [PATCH 35/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 11a7edef299..08e4c3d530c 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -378,20 +378,28 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(list.getParent()); refreshTable(target); + + // Check if only one choice is available for the selected Primary Sector if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, - k -> new ArrayList<>()); - ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); - logger.info("Choices: " + this.getChoices("")); - if (this.getChoices("").size() == 1) - { - activitySector = getAmpActivitySector(((List)this.getChoices("")).get(0)); - setModel.getObject().add(activitySector); + List choices = (List) this.getChoices(""); + logger.info("Choices: " + choices); + + if (choices.size() == 1) { + // Automatically add the only available Secondary Sector to the secondary table + AmpActivitySector secondarySector = getAmpActivitySector(choices.get(0)); + setModel.getObject().add(secondarySector); triggerUpdateEvent(setModel.getObject(), sectorClassification); target.add(list.getParent()); refreshTable(target); } } + + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); + ((List)this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(activitySector); + + } } From d2cac2d28ab1120c5105501bc2990ce76fb017f2 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 17:33:48 +0300 Subject: [PATCH 36/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 1 + .../tables/AmpSectorsFormTableFeature.java | 28 ++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 4a9707aac1d..a7e9dc781cb 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -65,6 +65,7 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel selectedSectors, if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) sectorsByClassification.add(ampActivitySector.getSectorId()); } + logger.info("Selected sectors: " + selectedSectors); + logger.info("Selected sectors by classification: " + sectorsByClassification); updateListener.onUpdate(sectorsByClassification); } @@ -380,19 +382,19 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { refreshTable(target); // Check if only one choice is available for the selected Primary Sector - if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - List choices = (List) this.getChoices(""); - logger.info("Choices: " + choices); - - if (choices.size() == 1) { - // Automatically add the only available Secondary Sector to the secondary table - AmpActivitySector secondarySector = getAmpActivitySector(choices.get(0)); - setModel.getObject().add(secondarySector); - triggerUpdateEvent(setModel.getObject(), sectorClassification); - target.add(list.getParent()); - refreshTable(target); - } - } +// if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { +// List choices = (List) this.getChoices(""); +// logger.info("Choices: " + choices); +// +// if (choices.size() == 1) { +// // Automatically add the only available Secondary Sector to the secondary table +// AmpActivitySector secondarySector = getAmpActivitySector(choices.get(0)); +// setModel.getObject().add(secondarySector); +// triggerUpdateEvent(setModel.getObject(), sectorClassification); +// target.add(list.getParent()); +// refreshTable(target); +// } +// } if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, From ecea39e8ffe7df89ee03fcdfe67330ba27d5d757 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 19:17:38 +0300 Subject: [PATCH 37/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 1 + 1 file changed, 1 insertion(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 51f1705e832..d703a4747a6 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -410,6 +410,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); + logger.info("Choices: " + choices); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); From 146871aeb93436f6e5a2be11352d286ba5fddbe8 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 20:19:54 +0300 Subject: [PATCH 38/85] AMP-30574 Map primary and secondary sectors --- .../items/AmpFundingGroupFeaturePanel.java | 32 ++--- .../AmpDonorFundingFormSectionFeature.java | 128 +++++++++--------- .../tables/AmpSectorsFormTableFeature.java | 19 ++- .../onepager/models/AmpSectorSearchModel.java | 1 + 4 files changed, 98 insertions(+), 82 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/items/AmpFundingGroupFeaturePanel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/items/AmpFundingGroupFeaturePanel.java index 55adeafce8a..43a3b644ce7 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/items/AmpFundingGroupFeaturePanel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/items/AmpFundingGroupFeaturePanel.java @@ -33,17 +33,17 @@ public class AmpFundingGroupFeaturePanel extends AmpFeaturePanel fundingRoleModel; private Integer tabIndex; - + public ListEditor getList() { return list; } - + public Integer getMaxFundingItemIndexFromList() { Integer max = null; for (AmpFunding af : list.items) { if (max == null) max = af.getIndex(); - if (max < af.getIndex()) + if (max < af.getIndex()) max = af.getIndex(); } return max; @@ -56,7 +56,7 @@ protected void onConfigure() { } - public AmpFundingGroupFeaturePanel(String id, String fmName, final IModel role, + public AmpFundingGroupFeaturePanel(String id, String fmName, final IModel role, IModel> fundsModel, final IModel model,final IModel am, final AmpDonorFundingFormSectionFeature parent) { super(id, model, fmName, true); fundingOrgModel = model; @@ -71,14 +71,14 @@ public boolean condition(AmpFunding item) { && item.getSourceRole().getAmpRoleId().equals(role.getObject().getAmpRoleId()); } }; - + list = new ListEditor("listFunding", setModel) { @Override protected void onPopulateItem( org.dgfoundation.amp.onepager.components.ListItem item) { AmpFundingItemFeaturePanel fundingItemFeature; try { - + fundingItemFeature = new AmpFundingItemFeaturePanel( "fundingItem", "Funding Item", item.getModel(), am, parent,item.getIndex()); @@ -89,35 +89,35 @@ protected void onPopulateItem( } }; add(list); - + final Boolean isTabView=FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.ACTIVITY_FORM_FUNDING_SECTION_DESIGN); - - AmpAjaxLinkField addNewFunding= new AmpAjaxLinkField("addAnotherFunding","New Funding Item","New Funding Item") { + + AmpAjaxLinkField addNewFunding= new AmpAjaxLinkField("addAnotherFunding","New Funding Item","New Funding Item") { private static final long serialVersionUID = 1L; @Override protected void onClick(AjaxRequestTarget target) { - if (fundsModel.getObject().size() > 0) { + if (!fundsModel.getObject().isEmpty()) { AmpFunding funding = new AmpFunding(); funding.setAmpDonorOrgId(model.getObject()); funding.setSourceRole(role.getObject()); - + parent.addFundingItem(funding); target.add(parent); target.appendJavaScript(OnePagerUtil.getToggleChildrenJS(parent)); - if (isTabView) { + if (Boolean.TRUE.equals(isTabView)) { int index = parent.calculateTabIndex(funding.getAmpDonorOrgId(), funding.getSourceRole()); - + target.appendJavaScript("switchTabs("+ index +");"); } } } }; - + add(addNewFunding); } - + public IModel getRole() { return fundingRoleModel; } @@ -129,5 +129,5 @@ public void setTabIndex(Integer index) { public Integer getTabIndex() { return tabIndex; } - + } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpDonorFundingFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpDonorFundingFormSectionFeature.java index 8bb08e96b24..f1910343efd 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpDonorFundingFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpDonorFundingFormSectionFeature.java @@ -72,18 +72,18 @@ /** * The donor funding section of the activity form. Includes selecting an org, * adding funding item, showing already added items - * - * + * + * * @author mpostelnicu@dgateway.org since Nov 3, 2010 */ public class AmpDonorFundingFormSectionFeature extends AmpFormSectionFeaturePanel implements AmpRequiredComponentContainer { private static final long serialVersionUID = 1L; private Map listItems = new TreeMap(); - + protected ListEditor orgRolelist; protected ListEditor tabsList; - + private IModel> orgRoleModel; private IModel> fundingOrgRoleModel; private AbstractReadOnlyModel> listModel; @@ -116,11 +116,11 @@ public String[] getRoleFilter() { return roleFilter; } - public void switchOrg(ListItem item, AmpFunding funding, AmpOrganisation newOrg, + public void switchOrg(ListItem item, AmpFunding funding, AmpOrganisation newOrg, AmpRole role, AjaxRequestTarget target) { AmpFundingGroupFeaturePanel existingFundGrp = getExistingFundingGroup(funding); - + if (existingFundGrp != null) { existingFundGrp.getList().remove(item); existingFundGrp.getList().updateModel(); @@ -153,9 +153,9 @@ public void switchOrg(ListItem item, AmpFunding funding, AmpOrganisation newOrg, target.add(orgRolelist.getParent()); } } - + /** - * Removes an item from a list editor + * Removes an item from a list editor * @param listEditor * @param orgRole * @param target @@ -200,23 +200,23 @@ public void deleteTab(AmpOrgRole ampOrgRole, AjaxRequestTarget target) { listItems.remove(ampOrgRole); deteleItem(tabsList, ampOrgRole, target); } - + public void updateFundingGroups(AmpOrgRole ampOrgRole, AjaxRequestTarget target) { AmpFundingGroupFeaturePanel existingFundingGroup = getExistingFundingGroup(ampOrgRole); boolean found = existingFundingGroup != null && existingFundingGroup.getList().size() > 0; - + if (!found) { // cleanup tab related data deleteTab(ampOrgRole, target); - + Set roles = fundingOrgRoleModel.getObject(); for (Iterator it2 = roles.iterator(); it2.hasNext();) { AmpOrgRole role = it2.next(); if (role.getRole().getRoleCode().equals(Constants.FUNDING_AGENCY) - && role.compareTo(ampOrgRole) == 0) { + && role.compareTo(ampOrgRole) == 0) { it2.remove(); send(getPage(), Broadcast.BREADTH, new DonorFundingRolesEvent(target)); - + if(this.originalSearchOrganizationSelector != null) { this.originalSearchOrganizationSelector.setVisibilityAllowed(true); target.add(this.originalSearchOrganizationSelector); @@ -224,7 +224,7 @@ public void updateFundingGroups(AmpOrgRole ampOrgRole, AjaxRequestTarget target) break; } } - + send(getPage(), Broadcast.BREADTH, new OrganisationUpdateEvent(target)); send(getPage(), Broadcast.BREADTH, new GPINiSurveyListUpdateEvent(target)); } @@ -242,17 +242,17 @@ public void setTemplateFilter(AmpTemplatesVisibility template) { public AmpDonorFundingFormSectionFeature(String id, String fmName, final IModel am) throws Exception { super(id, fmName, am); - + isTabsView = FeaturesUtil.getGlobalSettingValueBoolean(GlobalSettingsConstants.ACTIVITY_FORM_FUNDING_SECTION_DESIGN); - + final String expandAllKey = TranslatorWorker.generateTrnKey("Expand all"); final AjaxLink expandAllLink = new AjaxLink("expandDonorItems"){ - + final String javascript = "$(this).parents('div:eq(2)').find('.collapsable').show();$('#expandDonorItems-editor').hide();$('#expandDonorItems').hide();$('#collapseDonorItems').show();"; public void onClick(AjaxRequestTarget target) { - //we don't need any action to be prepended here + //we don't need any action to be prepended here } - + @Override protected void onConfigure() { super.onConfigure(); @@ -260,15 +260,15 @@ protected void onConfigure() { } }; add(expandAllLink); - - + + final String collapseAllKey = TranslatorWorker.generateTrnKey("Collapse all"); final AjaxLink collapseAllLink = new AjaxLink("collapseDonorItems"){ final String javascript = "$(this).parents('div:eq(2)').find('.collapsable').hide();$('#expandDonorItems').show();$('#collapseDonorItems').hide();$('#collapseDonorItems-editor').hide();"; public void onClick(AjaxRequestTarget target) { - //we don't need any action to be prepended here + //we don't need any action to be prepended here } - + @Override protected void onConfigure() { super.onConfigure(); @@ -276,58 +276,58 @@ protected void onConfigure() { } }; add(collapseAllLink); - + // group fields in FM under "Proposed Project Cost" - + fundingModel = new PropertyModel>(am, "funding"); if (fundingModel.getObject() == null) fundingModel.setObject(new LinkedHashSet()); orgRoleModel = new PropertyModel>(am, "orgrole"); fundingOrgRoleModel = new AmpFundingGroupModel(fundingModel, this); - + final WebMarkupContainer wmc = new WebMarkupContainer("container"); wmc.setOutputMarkupId(true); final WebMarkupContainer overviewLinkContainer = new WebMarkupContainer("overviewLinkContainer"); overviewLinkContainer.setOutputMarkupId(true); - final ExternalLink overviewTab = new ExternalLink("overviewLink", "#tab0", TranslatorWorker.translateText("Overview")); + final ExternalLink overviewTab = new ExternalLink("overviewLink", "#tab0", TranslatorWorker.translateText("Overview")); overviewTab.setOutputMarkupId(true); wmc.setOutputMarkupId(true); - + overviewLinkContainer.add(overviewTab); wmc.add(overviewLinkContainer); add(wmc); - + tabsList = new ListEditor("donorItemsForTabs", fundingOrgRoleModel) { private static final long serialVersionUID = -206108834217117807L; - + @Override protected void onPopulateItem(ListItem item) { AmpOrganisation org = item.getModel().getObject().getOrganisation(); String roleCode = item.getModel().getObject().getRole().getRoleCode(); - + ExternalLink l = new ExternalLink("linkForTabs", "#tab" + (item.getIndex() + 1)); l.add(new AttributePrepender("title", new Model(org.getName()), "")); String translatedRoleCode = TranslatorWorker.translateText(roleCode); - + Label label = new Label("tabsLabel", new Model(org.getAcronym())); - + Label subScript = new Label("tabsOrgRole", new Model(translatedRoleCode)); subScript.add(new AttributePrepender("class", new Model("subscript_role"), "")); l.add(label); l.add(subScript); - + item.add(l); } }; tabsList.setVisibilityAllowed(isTabsView); wmc.add(tabsList); - + AmpOverviewSection overviewSection = new AmpOverviewSection("overviewSection", "Overview Section", am) { @Override protected void onConfigure() { @@ -339,9 +339,9 @@ protected void onConfigure() { }; overviewSection.add(new AttributePrepender("data-is_tab", new Model("true"), "")); add(overviewSection); - + getRequiredFormComponents().addAll(overviewSection.getRequiredFormComponents()); - + orgRolelist = new ListEditor("listFunding", fundingOrgRoleModel) { @Override protected void onPopulateItem(ListItem item) { @@ -379,7 +379,7 @@ protected void onPopulateItem(ListItem item) { public void addItem(AmpOrgRole orgRole) { addToOrganisationSection(orgRole.getOrganisation()); addItemToList(orgRole.getOrganisation(), orgRole); - + orgRolelist.updateModel(); } }; @@ -407,12 +407,12 @@ protected String getAcronym(AmpOrganisation choice) { public void onSelect(AjaxRequestTarget target, AmpOrganisation choice) { addToOrganisationSection(choice); orgRolelist.addItem(findAmpOrgRole(choice, getSelectedAmpRole())); - + target.appendJavaScript(OnePagerUtil.getToggleChildrenJS(AmpDonorFundingFormSectionFeature.this)); send(getPage(), Broadcast.BREADTH, new OrganisationUpdateEvent(target)); target.add(AmpDonorFundingFormSectionFeature.this); - + if (isTabsView){ int index = calculateTabIndex(choice, getSelectedAmpRole()); target.appendJavaScript("switchTabs("+ index +");"); @@ -464,9 +464,9 @@ protected void onClick(AjaxRequestTarget target) { AmpOrganisation choice = (AmpOrganisation) orgRoleSelector.getOrgSelect().getChoiceContainer() .getModelObject(); orgRolelist.addItem(findAmpOrgRole(choice, getSelectedAmpRole())); - + target.appendJavaScript(OnePagerUtil.getToggleChildrenJS(AmpDonorFundingFormSectionFeature.this)); - + if (isTabsView) { target.add(AmpDonorFundingFormSectionFeature.this); int index = calculateTabIndex(choice, getSelectedAmpRole()); @@ -494,7 +494,7 @@ public PropertyModel> getFundingModel() { public List> getRequiredFormComponents() { return requiredFormComponents; } - + public void addItemToList(AmpOrganisation org, AmpOrgRole ampOrgRole) { AmpFunding funding = new AmpFunding(); if (ampOrgRole != null) { @@ -510,14 +510,14 @@ public void addItemToList(AmpOrganisation org, AmpOrgRole ampOrgRole) { funding.setMtefProjections(new HashSet()); funding.setFundingDetails(new HashSet()); funding.setGroupVersionedFunding(System.currentTimeMillis()); - + // if it is a ssc activity we set a default type of assistance if (ActivityUtil.ACTIVITY_TYPE_SSC.equals(((AmpAuthWebSession) getSession()).getFormType())) { Collection categoryValues = CategoryManagerUtil .getAmpCategoryValueCollectionByKey(CategoryConstants.TYPE_OF_ASSISTENCE_KEY); funding.setTypeOfAssistance(categoryValues.iterator().next()); } - + AmpFundingGroupFeaturePanel existingFundingGroup = getExistingFundingGroup(funding); if (existingFundingGroup != null) { funding.setIndex(existingFundingGroup.getMaxFundingItemIndexFromList() + 1); @@ -563,24 +563,24 @@ public void addToOrganisationSection(AmpOrganisation org) { } } - + private void configureTranslationMode (AjaxLink link,String key, String javascript) { if (TranslatorUtil.isTranslatorMode(getSession())){ link.setOutputMarkupId(true); link.add(new AttributeAppender("style", new Model("text-decoration: underline; color: #0CAD0C;"), "")); link.add(new AttributeModifier("key", key)); link.add(new AttributeModifier("onclick", "spawnEditBox(this.id,\""+javascript+"\")")); - - + + } else{ link.add(AttributeModifier.remove("key")); link.add(AttributeModifier.remove("onclick")); link.add(new AttributeModifier("onclick", javascript)); - + } } - + public void setOriginalSearchOrganizationSelector(AmpSearchOrganizationComponent selector) { this.originalSearchOrganizationSelector = selector; } @@ -588,48 +588,46 @@ public void setOriginalSearchOrganizationSelector(AmpSearchOrganizationComponent public int calculateTabIndex(AmpOrganisation choice, AmpRole role) { int index = -1; int newIndex = 0; - Iterator tabs = tabsList.items.iterator(); - while (tabs.hasNext()) { - AmpOrgRole o = tabs.next(); - if (o.getOrganisation().getIdentifier().equals(choice.getAmpOrgId()) && o.getRole().equals(role)) { - index = newIndex; - break; - } - newIndex++; - } + for (AmpOrgRole o : tabsList.items) { + if (o.getOrganisation().getIdentifier().equals(choice.getAmpOrgId()) && o.getRole().equals(role)) { + index = newIndex; + break; + } + newIndex++; + } //we only increment one if the overview tab is visible if(index !=-1 && isOverviewVisible.value){ index ++; //oveview is the first tab } return index; } - + protected AmpFundingGroupFeaturePanel getExistingFundingGroup(AmpFunding funding) { return getExistingFundingGroup(findAmpOrgRole(funding.getAmpDonorOrgId(), funding.getSourceRole())); } - + protected AmpFundingGroupFeaturePanel getExistingFundingGroup(AmpOrgRole ampOrgRole) { return listItems.get(ampOrgRole); } - + public AmpOrgRole findAmpOrgRole(AmpOrganisation org, AmpRole role) { if(org == null || role == null) { return null; } for (AmpOrgRole ampOrgRole : orgRoleModel.getObject()) { - if (ampOrgRole.getOrganisation().getIdentifier().equals(org.getIdentifier()) + if (ampOrgRole.getOrganisation().getIdentifier().equals(org.getIdentifier()) && ampOrgRole.getRole().getIdentifier().equals(role.getIdentifier())) { return (AmpOrgRole) ampOrgRole; } } return null; } - + public void addFundingItem(AmpFunding funding) { if (funding == null) return; orgRolelist.addItem(findAmpOrgRole(funding.getAmpDonorOrgId(), funding.getSourceRole())); } - + protected AmpRole getSelectedAmpRole() { AmpRole role = orgRoleSelector.getRoleSelect().getModel().getObject(); if (role == null) { diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index d703a4747a6..d2bfb359629 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -452,7 +452,7 @@ public Integer getChoiceLevel(AmpSector choice) { //For mappings between sectors with different classifications, we configure the search to show only the sectors // mapped to the selected sectors from the primary classification in dropdown list. if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - List primarySectorsSelected = new ArrayList(); + List primarySectorsSelected = new ArrayList<>(); for (AmpActivitySector ampActivitySector : setModel.getObject()) { if (ampActivitySector.getClassificationConfig().getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { primarySectorsSelected.add(ampActivitySector.getSectorId()); @@ -465,6 +465,23 @@ public Integer getChoiceLevel(AmpSector choice) { } add(this.searchSectors); + + + // Check if only one choice is available for the selected Primary Sector + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); + logger.info("Choices: " + choices); + + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + setModel.getObject().add(newSector); + } + } + } } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 127f31bac0d..5e0f5daab62 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -81,6 +81,7 @@ protected Collection load() { List list = crit.list(); ret = (Collection) createTreeView(list); + getParams().put(PARAM.DST_SECTORS_FOUND, ret); } catch (HibernateException e) { throw new RuntimeException(e); } finally { From 84d741e3b74bb1f3606f9fe872b7fd9a077156ed Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 20:28:17 +0300 Subject: [PATCH 39/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index d2bfb359629..ef3751b5112 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -471,14 +471,16 @@ public Integer getChoiceLevel(AmpSector choice) { if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); logger.info("Choices: " + choices); - - if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - setModel.getObject().add(newSector); + if (choices != null) { + + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + setModel.getObject().add(newSector); + } } } } From 1b28c1a579f657782dd31f323c78843c6f7825db Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 20:48:00 +0300 Subject: [PATCH 40/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index ef3751b5112..4a884f50a0d 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -381,20 +381,9 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { target.add(list.getParent()); refreshTable(target); - // Check if only one choice is available for the selected Primary Sector -// if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { -// List choices = (List) this.getChoices(""); -// logger.info("Choices: " + choices); -// -// if (choices.size() == 1) { -// // Automatically add the only available Secondary Sector to the secondary table -// AmpActivitySector secondarySector = getAmpActivitySector(choices.get(0)); -// setModel.getObject().add(secondarySector); -// triggerUpdateEvent(setModel.getObject(), sectorClassification); -// target.add(list.getParent()); -// refreshTable(target); -// } -// } + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + populateSecondarySectorsFor1Choice(target,sectorClassification); + } if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, @@ -410,7 +399,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - logger.info("Choices: " + choices); + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); @@ -468,22 +457,27 @@ public Integer getChoiceLevel(AmpSector choice) { // Check if only one choice is available for the selected Primary Sector - if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { - List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); - logger.info("Choices: " + choices); - if (choices != null) { - - if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - setModel.getObject().add(newSector); - } - } - } +// if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { +// +// } + } + + private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) + { + List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); + logger.info("Choices: " + choices); + if (choices != null) { + + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + setModel.getObject().add(newSector); } + } + } } From 27d9187c065a7491cb19cd3ac46b95340c99a095 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 20:49:41 +0300 Subject: [PATCH 41/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 4a884f50a0d..1f0020edeaa 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -381,7 +381,7 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { target.add(list.getParent()); refreshTable(target); - if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { + if (sectorClassification.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { populateSecondarySectorsFor1Choice(target,sectorClassification); } From debb07bab1f5c2a47a1729895cc8c65bc98e60af Mon Sep 17 00:00:00 2001 From: brianbrix Date: Thu, 13 Jun 2024 20:53:20 +0300 Subject: [PATCH 42/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 1 + 1 file changed, 1 insertion(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 1f0020edeaa..4e937aea021 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -478,6 +478,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } } } + target.add(list.getParent()); } From 493d6fd4ac9969a9655fde8e86f38a82022f7212 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 08:17:23 +0300 Subject: [PATCH 43/85] AMP-30574 Map primary and secondary sectors --- .../tables/AmpSectorsFormTableFeature.java | 69 ++++++++++++++----- .../onepager/models/AmpSectorSearchModel.java | 4 +- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 4e937aea021..fd563285102 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -4,6 +4,7 @@ */ package org.dgfoundation.amp.onepager.components.features.tables; +import org.apache.ecs.html.A; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.ajax.markup.html.AjaxIndicatorAppender; @@ -27,6 +28,7 @@ import org.dgfoundation.amp.onepager.util.AmpDividePercentageField; import org.dgfoundation.amp.onepager.util.FMUtil; import org.dgfoundation.amp.onepager.yui.AmpAutocompleteFieldPanel; +import org.digijava.kernel.persistence.PersistenceManager; import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.helper.FormatHelper; import org.digijava.module.aim.util.AmpAutoCompleteDisplayable; @@ -38,7 +40,13 @@ import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; import org.digijava.module.aim.util.SectorUtil; +import org.hibernate.Criteria; +import org.hibernate.HibernateException; +import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.criterion.Junction; +import org.hibernate.criterion.Restrictions; +import org.hibernate.type.LongType; import org.jetbrains.annotations.NotNull; import static org.digijava.kernel.ampapi.endpoints.activity.ActivityEPConstants.MAXIMUM_PERCENTAGE; @@ -84,14 +92,11 @@ public void setDeleteListener(ISectorTableDeleteListener listener) { protected void triggerUpdateEvent(Set selectedSectors, AmpClassificationConfiguration sectorClassification) { if (updateListener != null) { - List sectorsByClassification = new ArrayList(); + List sectorsByClassification = new ArrayList<>(); for (AmpActivitySector ampActivitySector : selectedSectors) { if (ampActivitySector.getClassificationConfig().getId().equals(sectorClassification.getId())) sectorsByClassification.add(ampActivitySector.getSectorId()); } - logger.info("Selected sectors: " + selectedSectors); - logger.info("Selected sectors by classification: " + sectorsByClassification); - updateListener.onUpdate(sectorsByClassification); } } @@ -378,13 +383,15 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { if (setModel.getObject() == null) setModel.setObject(new HashSet<>()); setModel.getObject().add(activitySector); triggerUpdateEvent(setModel.getObject(), sectorClassification); - target.add(list.getParent()); - refreshTable(target); - if (sectorClassification.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { + this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); populateSecondarySectorsFor1Choice(target,sectorClassification); } + target.add(list.getParent()); + refreshTable(target); + + if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { this.getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, k -> new ArrayList<>()); @@ -464,21 +471,45 @@ public Integer getChoiceLevel(AmpSector choice) { private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) { - List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); - logger.info("Choices: " + choices); - if (choices != null) { - - if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - setModel.getObject().add(newSector); - } + + AmpSector selectedSector =(AmpSector) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); +// List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); + List choices = searchSectorsDstFromMapping(selectedSector); + logger.info("Choices found: " + selectedSector); + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + setModel.getObject().add(newSector); } } target.add(list.getParent()); + + + } + + + private List searchSectorsDstFromMapping(AmpSector srcSector) { + Session session = PersistenceManager.getRequestDBSession(); + List dstSectorIds = new ArrayList<>(); + String hql = "SELECT sm.dstSector FROM AmpSectorMapping sm WHERE sm.srcSector.ampSectorId = :srcSectorId"; + + try { + Query query = session.createQuery(hql); + query.setParameter("srcSectorId", srcSector.getAmpSectorId(), LongType.INSTANCE); + dstSectorIds = query.list(); + +// for (Object obj : resultList) { +// dstSectorIds.add((AmpSector) obj); +// } + } catch (HibernateException e) { + // Handle the exception + e.printStackTrace(); + } + + return dstSectorIds; } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 5e0f5daab62..0cbfddad546 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -30,6 +30,7 @@ public class AmpSectorSearchModel extends public enum PARAM implements AmpAutoCompleteModelParam { SECTOR_SCHEME, + CURRENT_SRC_SECTOR_SELECTED, SRC_SECTOR_SELECTED, // used in case of sector mapping exists DST_SECTOR_SELECTED, DST_SECTORS_FOUND @@ -46,9 +47,8 @@ public AmpSectorSearchModel(String input,String language, @Override protected Collection load() { - Collection ret = null; + Collection ret= new ArrayList<>(); try { - ret = new ArrayList(); session = PersistenceManager.getSession(); session.enableFilter("isDeletedFilter").setParameter("deleted", Boolean.FALSE); From c39648a53581f605d2c011f25c7d2db83995ba84 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 08:24:56 +0300 Subject: [PATCH 44/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index fd563285102..ebdae344a28 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -475,7 +475,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla AmpSector selectedSector =(AmpSector) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); // List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); List choices = searchSectorsDstFromMapping(selectedSector); - logger.info("Choices found: " + selectedSector); + logger.info("Choices found: " + choices); if (choices.size() == 1) { for (AmpSector secondarySector : choices) { AmpActivitySector newSector = new AmpActivitySector(); From 4c276ed6d14753ceecfbc40d88c3a99881264a47 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 08:46:01 +0300 Subject: [PATCH 45/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 63 ++++++++++++++++++- .../tables/AmpSectorsFormTableFeature.java | 43 +------------ 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index a7e9dc781cb..d3dfec172ba 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -4,6 +4,7 @@ */ package org.dgfoundation.amp.onepager.components.features.sections; +import java.util.ArrayList; import java.util.List; import org.apache.wicket.ajax.AjaxRequestTarget; @@ -12,13 +13,20 @@ import org.apache.wicket.request.cycle.RequestCycle; import org.dgfoundation.amp.onepager.components.features.tables.AmpSectorsFormTableFeature; import org.dgfoundation.amp.onepager.interfaces.ISectorTableDeleteListener; +import org.dgfoundation.amp.onepager.models.AmpSectorSearchModel; import org.dgfoundation.amp.onepager.util.AmpFMTypes; +import org.digijava.kernel.persistence.PersistenceManager; +import org.digijava.module.aim.dbentity.AmpActivitySector; import org.digijava.module.aim.dbentity.AmpActivityVersion; import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; import org.digijava.module.aim.dbentity.AmpSector; import org.digijava.module.aim.util.SectorUtil; import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.type.LongType; /** * @author mpostelnicu@dgateway.org @@ -31,6 +39,8 @@ public class AmpSectorsFormSectionFeature extends AmpFormSectionFeaturePanel private AmpSectorsFormTableFeature primarySectorsTable; private AmpSectorsFormTableFeature secondarySectorsTable; + AmpClassificationConfiguration primaryConf = new AmpClassificationConfiguration(); + AmpClassificationConfiguration secondaryConf = new AmpClassificationConfiguration(); /** * @param id @@ -47,8 +57,7 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel allClassificationConfigs = SectorUtil.getAllClassificationConfigsOrdered(); - AmpClassificationConfiguration primaryConf = new AmpClassificationConfiguration(); - AmpClassificationConfiguration secondaryConf = new AmpClassificationConfiguration(); + for (AmpClassificationConfiguration conf : allClassificationConfigs) { if (conf.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { primaryConf = conf; @@ -86,7 +95,57 @@ public void onUpdate(List data) { secondarySectorsTable.updateBasedOnData(data); target.add(secondarySectorsTable.getSearchSectors()); } + populateSecondarySectorsFor1Choice(secondarySectorsTable,primarySectorsTable,target,secondaryConf); + + } + + + + + } + + private void populateSecondarySectorsFor1Choice(AmpSectorsFormTableFeature secondarySectorsTable,AmpSectorsFormTableFeature primarySectorsTable,AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) + { + + AmpSector selectedSector =(AmpSector) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); + if (selectedSector!=null) { + List choices = searchSectorsDstFromMapping(selectedSector); + logger.info("Choices found: " + choices); + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + secondarySectorsTable.getSetModel().getObject().add(newSector); + } + } + target.add(secondarySectorsTable.getList().getParent()); } + + + } + + + private List searchSectorsDstFromMapping(AmpSector srcSector) { + Session session = PersistenceManager.getRequestDBSession(); + List dstSectorIds = new ArrayList<>(); + String hql = "SELECT sm.dstSector FROM AmpSectorMapping sm WHERE sm.srcSector.ampSectorId = :srcSectorId"; + + try { + Query query = session.createQuery(hql); + query.setParameter("srcSectorId", srcSector.getAmpSectorId(), LongType.INSTANCE); + dstSectorIds = query.list(); + +// for (Object obj : resultList) { +// dstSectorIds.add((AmpSector) obj); +// } + } catch (HibernateException e) { + // Handle the exception + e.printStackTrace(); + } + + return dstSectorIds; } /** diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index ebdae344a28..7c85b140b97 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -385,7 +385,7 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { triggerUpdateEvent(setModel.getObject(), sectorClassification); if (sectorClassification.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); - populateSecondarySectorsFor1Choice(target,sectorClassification); +// populateSecondarySectorsFor1Choice(target,sectorClassification); } target.add(list.getParent()); @@ -469,48 +469,7 @@ public Integer getChoiceLevel(AmpSector choice) { // } } - private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) - { - - AmpSector selectedSector =(AmpSector) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); -// List choices = (List) this.searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); - List choices = searchSectorsDstFromMapping(selectedSector); - logger.info("Choices found: " + choices); - if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(setModel.getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - setModel.getObject().add(newSector); - } - } - target.add(list.getParent()); - - - } - - private List searchSectorsDstFromMapping(AmpSector srcSector) { - Session session = PersistenceManager.getRequestDBSession(); - List dstSectorIds = new ArrayList<>(); - String hql = "SELECT sm.dstSector FROM AmpSectorMapping sm WHERE sm.srcSector.ampSectorId = :srcSectorId"; - - try { - Query query = session.createQuery(hql); - query.setParameter("srcSectorId", srcSector.getAmpSectorId(), LongType.INSTANCE); - dstSectorIds = query.list(); - -// for (Object obj : resultList) { -// dstSectorIds.add((AmpSector) obj); -// } - } catch (HibernateException e) { - // Handle the exception - e.printStackTrace(); - } - - return dstSectorIds; - } } From bcbca49f95c03b67bc85d2e53960e8c28263b8cd Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 08:54:42 +0300 Subject: [PATCH 46/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 3 +++ .../features/tables/AmpSectorsFormTableFeature.java | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index d3dfec172ba..f5ef0db6086 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -88,6 +88,7 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel data) { + logger.info("IN UPDATE"); if (secondarySectorsTable != null) { // Update user interface AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); @@ -108,6 +109,8 @@ private void populateSecondarySectorsFor1Choice(AmpSectorsFormTableFeature secon { AmpSector selectedSector =(AmpSector) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); + logger.info("Selected sector: " + selectedSector); + if (selectedSector!=null) { List choices = searchSectorsDstFromMapping(selectedSector); logger.info("Choices found: " + choices); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 7c85b140b97..9acec039191 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -382,11 +382,11 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { AmpActivitySector activitySector = getAmpActivitySector(choice); if (setModel.getObject() == null) setModel.setObject(new HashSet<>()); setModel.getObject().add(activitySector); - triggerUpdateEvent(setModel.getObject(), sectorClassification); if (sectorClassification.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { - this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); -// populateSecondarySectorsFor1Choice(target,sectorClassification); + this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); } + triggerUpdateEvent(setModel.getObject(), sectorClassification); + target.add(list.getParent()); refreshTable(target); From d5abda2467d88768186125a2bd7302b69e8f0319 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 09:37:44 +0300 Subject: [PATCH 47/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index f5ef0db6086..d12355d8fa5 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -110,23 +110,28 @@ private void populateSecondarySectorsFor1Choice(AmpSectorsFormTableFeature secon AmpSector selectedSector =(AmpSector) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); logger.info("Selected sector: " + selectedSector); - - if (selectedSector!=null) { - List choices = searchSectorsDstFromMapping(selectedSector); - logger.info("Choices found: " + choices); - if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - secondarySectorsTable.getSetModel().getObject().add(newSector); + try { + if (selectedSector!=null) { + List choices = searchSectorsDstFromMapping(selectedSector); + logger.info("Choices found: " + choices); + if (choices.size() == 1) { + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + secondarySectorsTable.getSetModel().getObject().add(newSector); + } } + target.add(secondarySectorsTable.getList().getParent()); } - target.add(secondarySectorsTable.getList().getParent()); + }catch (Exception e) { + logger.error("Error",e); } + + } From 0c0bbcac93bbc0454883165add06e9700e6cbbb1 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 09:52:20 +0300 Subject: [PATCH 48/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 5 +++-- .../features/tables/AmpSectorsFormTableFeature.java | 3 +++ .../amp/onepager/models/AmpSectorSearchModel.java | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index d12355d8fa5..cb1d1bfd291 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -88,7 +88,6 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel data) { - logger.info("IN UPDATE"); if (secondarySectorsTable != null) { // Update user interface AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); @@ -96,7 +95,9 @@ public void onUpdate(List data) { secondarySectorsTable.updateBasedOnData(data); target.add(secondarySectorsTable.getSearchSectors()); } - populateSecondarySectorsFor1Choice(secondarySectorsTable,primarySectorsTable,target,secondaryConf); + if (this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.ACTION)=="add" || this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.ACTION)=="add") { + populateSecondarySectorsFor1Choice(secondarySectorsTable, primarySectorsTable, target, secondaryConf); + } } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 9acec039191..b95af045865 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -313,6 +313,8 @@ protected void onAjaxOnUpdate( @Override public void onClick(AjaxRequestTarget target) { AmpActivitySector sectorToDelete = item.getModelObject(); + getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.ACTION,"del"); + setModel.getObject().remove(sectorToDelete); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME) && (getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)!=null)) { ((List) getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).remove(sectorToDelete); @@ -382,6 +384,7 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { AmpActivitySector activitySector = getAmpActivitySector(choice); if (setModel.getObject() == null) setModel.setObject(new HashSet<>()); setModel.getObject().add(activitySector); + this.getModelParams().put(AmpSectorSearchModel.PARAM.ACTION,"add"); if (sectorClassification.getName().equals(AmpClassificationConfiguration.PRIMARY_CLASSIFICATION_CONFIGURATION_NAME)) { this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 0cbfddad546..2d95bf55d9b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -30,6 +30,7 @@ public class AmpSectorSearchModel extends public enum PARAM implements AmpAutoCompleteModelParam { SECTOR_SCHEME, + ACTION, CURRENT_SRC_SECTOR_SELECTED, SRC_SECTOR_SELECTED, // used in case of sector mapping exists DST_SECTOR_SELECTED, From d53021f9571c4bdfbf60046e9a58658136075cc7 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 10:07:45 +0300 Subject: [PATCH 49/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 7 ++++--- .../features/tables/AmpSectorsFormTableFeature.java | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index cb1d1bfd291..012ad6fdab7 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -96,7 +96,7 @@ public void onUpdate(List data) { target.add(secondarySectorsTable.getSearchSectors()); } if (this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.ACTION)=="add" || this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.ACTION)=="add") { - populateSecondarySectorsFor1Choice(secondarySectorsTable, primarySectorsTable, target, secondaryConf); + populateSecondarySectorsFor1Choice( target, secondaryConf); } } @@ -106,7 +106,7 @@ public void onUpdate(List data) { } - private void populateSecondarySectorsFor1Choice(AmpSectorsFormTableFeature secondarySectorsTable,AmpSectorsFormTableFeature primarySectorsTable,AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) + private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpClassificationConfiguration sectorClassification) { AmpSector selectedSector =(AmpSector) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); @@ -121,10 +121,11 @@ private void populateSecondarySectorsFor1Choice(AmpSectorsFormTableFeature secon newSector.setSectorId(secondarySector); newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same newSector.setClassificationConfig(sectorClassification); + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, newSector); secondarySectorsTable.getSetModel().getObject().add(newSector); } } - target.add(secondarySectorsTable.getList().getParent()); + target.add(secondarySectorsTable); } }catch (Exception e) { logger.error("Error",e); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index b95af045865..eb1e0556d4f 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -389,8 +389,6 @@ public void onSelect(AjaxRequestTarget target, AmpSector choice) { this.getModelParams().put(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED,choice); } triggerUpdateEvent(setModel.getObject(), sectorClassification); - - target.add(list.getParent()); refreshTable(target); From 65be43fc655e2a5fcadcff53ee47dc0528f37898 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 10:49:12 +0300 Subject: [PATCH 50/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 012ad6fdab7..9521c93c748 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -16,16 +16,14 @@ import org.dgfoundation.amp.onepager.models.AmpSectorSearchModel; import org.dgfoundation.amp.onepager.util.AmpFMTypes; import org.digijava.kernel.persistence.PersistenceManager; -import org.digijava.module.aim.dbentity.AmpActivitySector; -import org.digijava.module.aim.dbentity.AmpActivityVersion; -import org.digijava.module.aim.dbentity.AmpClassificationConfiguration; -import org.digijava.module.aim.dbentity.AmpSector; +import org.digijava.module.aim.dbentity.*; import org.digijava.module.aim.util.SectorUtil; import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.type.IntegerType; import org.hibernate.type.LongType; /** @@ -125,7 +123,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.getSetModel().getObject().add(newSector); } } - target.add(secondarySectorsTable); + target.add(secondarySectorsTable.getList().getParent()); } }catch (Exception e) { logger.error("Error",e); @@ -146,10 +144,19 @@ private List searchSectorsDstFromMapping(AmpSector srcSector) { Query query = session.createQuery(hql); query.setParameter("srcSectorId", srcSector.getAmpSectorId(), LongType.INSTANCE); dstSectorIds = query.list(); + if (dstSectorIds.isEmpty()) + { + AmpSectorScheme scheme = (AmpSectorScheme) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SECTOR_SCHEME); + + session = PersistenceManager.getSession(); + String sql = "select pi from " + + AmpSector.class.getName() + + " pi where pi.ampSecSchemeId=:schemeId and pi.parentSectorId IS null and (pi.deleted is null or pi.deleted = false)"; + query = session.createQuery(sql); + query.setParameter("schemeId", scheme.getAmpSecSchemeId(), LongType.INSTANCE); + dstSectorIds = query.list(); + } -// for (Object obj : resultList) { -// dstSectorIds.add((AmpSector) obj); -// } } catch (HibernateException e) { // Handle the exception e.printStackTrace(); From 3ebb00e25eab2db90be5a72557e6896393b79780 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 11:22:51 +0300 Subject: [PATCH 51/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 9521c93c748..5f11acf9c27 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -119,7 +119,8 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla newSector.setSectorId(secondarySector); newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same newSector.setClassificationConfig(sectorClassification); - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, newSector); + ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); + secondarySectorsTable.getSetModel().getObject().add(newSector); } } From 60934dec2536f756e92811849403c7eb920f3fb6 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 11:31:56 +0300 Subject: [PATCH 52/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 5f11acf9c27..decd00c7a1e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -119,6 +119,8 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla newSector.setSectorId(secondarySector); newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same newSector.setClassificationConfig(sectorClassification); + this.secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); secondarySectorsTable.getSetModel().getObject().add(newSector); From e95552658011278d1c82a164c47533e8527c172b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 11:44:36 +0300 Subject: [PATCH 53/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index decd00c7a1e..ccc5a610190 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -113,20 +113,24 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla if (selectedSector!=null) { List choices = searchSectorsDstFromMapping(selectedSector); logger.info("Choices found: " + choices); + + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + this.secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); + ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); + + secondarySectorsTable.getSetModel().getObject().add(newSector); + } + if (choices.size() == 1) { - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - this.secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, - k -> new ArrayList<>()); - ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); - - secondarySectorsTable.getSetModel().getObject().add(newSector); - } + + target.add(secondarySectorsTable.getList().getParent()); + } - target.add(secondarySectorsTable.getList().getParent()); } }catch (Exception e) { logger.error("Error",e); From c57eab60bf2dac867d6a4e9cdc7f737bf849c849 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 12:02:11 +0300 Subject: [PATCH 54/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 28 +++++++++---------- .../tables/AmpSectorsFormTableFeature.java | 5 ---- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index ccc5a610190..6a8f080c926 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -113,24 +113,24 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla if (selectedSector!=null) { List choices = searchSectorsDstFromMapping(selectedSector); logger.info("Choices found: " + choices); - - for (AmpSector secondarySector : choices) { - AmpActivitySector newSector = new AmpActivitySector(); - newSector.setSectorId(secondarySector); - newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same - newSector.setClassificationConfig(sectorClassification); - this.secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, - k -> new ArrayList<>()); - ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); - - secondarySectorsTable.getSetModel().getObject().add(newSector); - } - if (choices.size() == 1) { - + for (AmpSector secondarySector : choices) { + AmpActivitySector newSector = new AmpActivitySector(); + newSector.setSectorId(secondarySector); + newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same + newSector.setClassificationConfig(sectorClassification); + this.secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); + ((List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(newSector); + + secondarySectorsTable.getSetModel().getObject().add(newSector); + } target.add(secondarySectorsTable.getList().getParent()); } + secondarySectorsTable.updateBasedOnData(choices); + target.add(secondarySectorsTable.getSearchSectors()); + } }catch (Exception e) { logger.error("Error",e); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index eb1e0556d4f..8a9781c0259 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -463,11 +463,6 @@ public Integer getChoiceLevel(AmpSector choice) { add(this.searchSectors); - - // Check if only one choice is available for the selected Primary Sector -// if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { -// -// } } From 1e49775f146b6ffb5b4174f4a461c3c65b63db7a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 12:16:48 +0300 Subject: [PATCH 55/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 6a8f080c926..4ef6235161e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -128,7 +128,9 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - secondarySectorsTable.updateBasedOnData(choices); + List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + + secondarySectorsTable.updateBasedOnData(srcSectorSelected); target.add(secondarySectorsTable.getSearchSectors()); } From 2e0cb805d2e5bd364526ad53d1972df9daac25e1 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 12:50:45 +0300 Subject: [PATCH 56/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 4 +++- .../features/tables/AmpSectorsFormTableFeature.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 4ef6235161e..9a2d22e8962 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -130,7 +130,9 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + target.add(secondarySectorsTable.getSearchSectors()); } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 8a9781c0259..6c8dc1acd29 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -89,7 +89,7 @@ public void setDeleteListener(ISectorTableDeleteListener listener) { * @param selectedSectors the set of selected sectors * @param sectorClassification the current sector classification configuration */ - protected void triggerUpdateEvent(Set selectedSectors, + public void triggerUpdateEvent(Set selectedSectors, AmpClassificationConfiguration sectorClassification) { if (updateListener != null) { List sectorsByClassification = new ArrayList<>(); From ba34ea87655ecc600e16a21d0f85478d4d4bed99 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 12:52:42 +0300 Subject: [PATCH 57/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 9a2d22e8962..70d7f178d13 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -130,7 +130,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.updateBasedOnData(srcSectorSelected); secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); target.add(secondarySectorsTable.getSearchSectors()); From cb555a9d184d0ce0c83b8a3c5e5c617cc8051742 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 13:49:23 +0300 Subject: [PATCH 58/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 3 +++ .../features/tables/AmpSectorsFormTableFeature.java | 6 ++++++ .../amp/onepager/models/AmpSectorSearchModel.java | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 70d7f178d13..e10758d9a53 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -131,10 +131,13 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); target.add(secondarySectorsTable.getSearchSectors()); + } }catch (Exception e) { logger.error("Error",e); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 6c8dc1acd29..182f6404afc 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,6 +407,12 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); + List newChoices = (List)getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + if (newChoices!=null) + { + choices=newChoices; + choices2= new HashSet<>(newChoices); + } if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 2d95bf55d9b..2f2761f3e9b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -34,7 +34,7 @@ public enum PARAM implements AmpAutoCompleteModelParam { CURRENT_SRC_SECTOR_SELECTED, SRC_SECTOR_SELECTED, // used in case of sector mapping exists DST_SECTOR_SELECTED, - DST_SECTORS_FOUND + NEW_CHOICES, DST_SECTORS_FOUND } public AmpSectorSearchModel(String input,String language, From def68ada98452f0ec1c95ec2f08b24247f06f8cf Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 15:19:02 +0300 Subject: [PATCH 59/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 47 ++++++++++++++++++- .../tables/AmpSectorsFormTableFeature.java | 2 +- .../onepager/models/AmpSectorSearchModel.java | 7 +++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index e10758d9a53..b0e6590670e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -4,8 +4,7 @@ */ package org.dgfoundation.amp.onepager.components.features.sections; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.repeater.RepeatingView; @@ -13,10 +12,12 @@ import org.apache.wicket.request.cycle.RequestCycle; import org.dgfoundation.amp.onepager.components.features.tables.AmpSectorsFormTableFeature; import org.dgfoundation.amp.onepager.interfaces.ISectorTableDeleteListener; +import org.dgfoundation.amp.onepager.models.AbstractAmpAutoCompleteModel; import org.dgfoundation.amp.onepager.models.AmpSectorSearchModel; import org.dgfoundation.amp.onepager.util.AmpFMTypes; import org.digijava.kernel.persistence.PersistenceManager; import org.digijava.module.aim.dbentity.*; +import org.digijava.module.aim.util.AmpAutoCompleteDisplayable; import org.digijava.module.aim.util.SectorUtil; import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; @@ -112,6 +113,8 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla try { if (selectedSector!=null) { List choices = searchSectorsDstFromMapping(selectedSector); + choices = (List) createTreeView(choices); + logger.info("Choices found: " + choices); if (choices.size() == 1) { for (AmpSector secondarySector : choices) { @@ -149,6 +152,46 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } + protected void addToRootTree(Collection tree, AmpAutoCompleteDisplayable obj, + boolean searchHit) { + if (obj.getParent() == null) + tree.add(obj); + else { + if (searchHit) + obj.getVisibleSiblings().addAll(obj.getNonDeletedChildren()); + obj.getParent().getVisibleSiblings().add(obj); + addToRootTree(tree, obj.getParent(), false); + } + } + + protected void addToRootList(Collection list, AmpAutoCompleteDisplayable obj) { + list.add(obj); + Collection children = obj.getVisibleSiblings(); + for (AmpAutoCompleteDisplayable ampSector : children) { + addToRootList(list, ampSector); + } + } + + protected Collection createTreeView(Collection l) { + Boolean b = (Boolean) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AbstractAmpAutoCompleteModel.PARAM.EXACT_MATCH); + if (b != null && b) + return l; + Collection ret = new ArrayList(); + + Set root = new TreeSet(new AmpAutoCompleteDisplayable.AmpAutoCompleteComparator()); + for (Object o : l) + addToRootTree(root, (AmpAutoCompleteDisplayable) o, true); + for (Object o : root) { + addToRootList(ret, (AmpAutoCompleteDisplayable) o); + } + + return ret; + } + + + + + private List searchSectorsDstFromMapping(AmpSector srcSector) { Session session = PersistenceManager.getRequestDBSession(); List dstSectorIds = new ArrayList<>(); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 182f6404afc..cf5fa093e70 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -410,7 +410,7 @@ public Collection getChoices(String input) { List newChoices = (List)getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); if (newChoices!=null) { - choices=newChoices; + choices= newChoices; choices2= new HashSet<>(newChoices); } diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 2f2761f3e9b..490ea1cfdc4 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -15,6 +15,7 @@ import org.digijava.module.aim.dbentity.AmpSector; import org.digijava.module.aim.dbentity.AmpSectorMapping; import org.digijava.module.aim.dbentity.AmpSectorScheme; +import org.digijava.module.aim.util.AmpAutoCompleteDisplayable; import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Session; @@ -43,6 +44,7 @@ public AmpSectorSearchModel(String input,String language, // TODO Auto-generated constructor stub } + private static final long serialVersionUID = 8211300754918658832L; private Session session; @@ -81,6 +83,7 @@ protected Collection load() { if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); List list = crit.list(); + ret = (Collection) createTreeView(list); getParams().put(PARAM.DST_SECTORS_FOUND, ret); } catch (HibernateException e) { @@ -90,6 +93,10 @@ protected Collection load() { } return ret; } + public Collection createTree(Collection collection) + { + return createTreeView(collection); + } /* * Search for sectors that are mapped to the given sector From 98d291bf7eba5d905367a33939c85be7c0d1b768 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 15:29:52 +0300 Subject: [PATCH 60/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 6 +++--- .../amp/onepager/models/AmpSectorSearchModel.java | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index b0e6590670e..bc5d770bcc2 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -131,11 +131,11 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +// List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); +// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); target.add(secondarySectorsTable.getSearchSectors()); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 490ea1cfdc4..2a1b0c496fc 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -22,6 +22,8 @@ import org.hibernate.criterion.Junction; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author mpostelnicu@dgateway.org since Sep 28, 2010 @@ -37,6 +39,7 @@ public enum PARAM implements AmpAutoCompleteModelParam { DST_SECTOR_SELECTED, NEW_CHOICES, DST_SECTORS_FOUND } + Logger logger = LoggerFactory.getLogger(AmpSectorSearchModel.class); public AmpSectorSearchModel(String input,String language, Map params) { @@ -83,8 +86,10 @@ protected Collection load() { if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); List list = crit.list(); + logger.info("List here 1: " + list); ret = (Collection) createTreeView(list); + logger.info("List here: " + ret); getParams().put(PARAM.DST_SECTORS_FOUND, ret); } catch (HibernateException e) { throw new RuntimeException(e); From e15dafbf0d319a9e40dcba93ad0a3c2db2e6eb5a Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 15:39:18 +0300 Subject: [PATCH 61/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 86 ++++++++++++++++++- .../onepager/models/AmpSectorSearchModel.java | 4 - 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index bc5d770bcc2..fe4d01b2da4 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -21,9 +21,13 @@ import org.digijava.module.aim.util.SectorUtil; import org.dgfoundation.amp.onepager.interfaces.ISectorTableUpdateListener; +import org.hibernate.Criteria; import org.hibernate.HibernateException; import org.hibernate.Query; import org.hibernate.Session; +import org.hibernate.criterion.Junction; +import org.hibernate.criterion.Order; +import org.hibernate.criterion.Restrictions; import org.hibernate.type.IntegerType; import org.hibernate.type.LongType; @@ -113,9 +117,10 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla try { if (selectedSector!=null) { List choices = searchSectorsDstFromMapping(selectedSector); - choices = (List) createTreeView(choices); +// choices = (List) createTreeView(choices); logger.info("Choices found: " + choices); + load(); if (choices.size() == 1) { for (AmpSector secondarySector : choices) { AmpActivitySector newSector = new AmpActivitySector(); @@ -222,6 +227,85 @@ private List searchSectorsDstFromMapping(AmpSector srcSector) { return dstSectorIds; } + + protected Collection load() { + Collection ret= new ArrayList<>(); + Session session = PersistenceManager.getSession(); + try { + session.enableFilter("isDeletedFilter").setParameter("deleted", Boolean.FALSE); + + Integer maxResults = (Integer) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS); + AmpSectorScheme scheme = (AmpSectorScheme) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SECTOR_SCHEME); + Criteria crit = session.createCriteria(AmpSector.class); + crit.setCacheable(true); + Junction junction = Restrictions.conjunction().add( + Restrictions.and( + Restrictions.eq("ampSecSchemeId", scheme), + Restrictions.or( + Restrictions.isNull("deleted"), + Restrictions.eq( "deleted", Boolean.FALSE) + ))); + + List srcSectorSelected = (List) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + Junction junction2 = null; + if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { + List ids = searchSectorsDstFromMapping(srcSectorSelected); + if (!ids.isEmpty()) { + junction2 = Restrictions.conjunction().add( + Restrictions.in("ampSectorId", ids)); + } + } + + crit.add(junction); + if (junction2 != null) crit.add(junction2); + crit.addOrder(Order.asc("name")); + if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); + List list = crit.list(); + + logger.info("List here 1: " + list); + + ret = (Collection) createTreeView(list); + logger.info("List here: " + ret); + this.secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND, ret); + } catch (HibernateException e) { + throw new RuntimeException(e); + } finally { + session.disableFilter("isDeletedFilter"); + } + return ret; + } + + /* + * Search for sectors that are mapped to the given sector + * */ + private List searchSectorsDstFromMapping(List srcSectors) { + List ids = new ArrayList(); + Session session = PersistenceManager.getRequestDBSession(); + try { + Criteria crit = session.createCriteria(AmpSectorMapping.class); + crit.setCacheable(true); + + if (srcSectors != null && !srcSectors.isEmpty()) { + Junction junction = Restrictions.conjunction().add( + Restrictions.in("srcSector", srcSectors) + ); + + crit.add(junction); + List list = crit.list(); + if (list != null && !list.isEmpty()) { + for (AmpSectorMapping mapping : list) { + ids.add(mapping.getDstSector().getAmpSectorId()); + } + } + } + } catch (HibernateException e) { + throw new RuntimeException(e); + } finally { + session.disableFilter("isDeletedFilter"); + } + return ids; + } + /** * Updates the user interface when a deletion event is triggered. * This method is called when the onDelete method is invoked in the containing class. diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 2a1b0c496fc..4f98d600f29 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -98,10 +98,6 @@ protected Collection load() { } return ret; } - public Collection createTree(Collection collection) - { - return createTreeView(collection); - } /* * Search for sectors that are mapped to the given sector From 762f622dfff420754ec7b9507da1b78ed1e54a3d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 16:59:05 +0300 Subject: [PATCH 62/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index fe4d01b2da4..72f9bdf8be8 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -116,7 +116,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { - List choices = searchSectorsDstFromMapping(selectedSector); + List choices = (List)load(); // choices = (List) createTreeView(choices); logger.info("Choices found: " + choices); @@ -136,11 +136,11 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } -// List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.updateBasedOnData(srcSectorSelected); -// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); target.add(secondarySectorsTable.getSearchSectors()); From d7162a9b0fc2dfd2616df30d44a634dae4a9e1db Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 17:14:04 +0300 Subject: [PATCH 63/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 13 ++++++------- .../features/tables/AmpSectorsFormTableFeature.java | 3 ++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 72f9bdf8be8..17a102dd2b7 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -117,11 +117,12 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla try { if (selectedSector!=null) { List choices = (List)load(); -// choices = (List) createTreeView(choices); + List sectorChoices = searchSectorsDstFromMapping(selectedSector); + logger.info("Choices found: " + choices); - load(); - if (choices.size() == 1) { + logger.info("Sector Choices found: " + sectorChoices); + if (sectorChoices.size() == 1) { for (AmpSector secondarySector : choices) { AmpActivitySector newSector = new AmpActivitySector(); newSector.setSectorId(secondarySector); @@ -138,9 +139,9 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); +// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); target.add(secondarySectorsTable.getSearchSectors()); @@ -262,10 +263,8 @@ protected Collection load() { if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); List list = crit.list(); - logger.info("List here 1: " + list); ret = (Collection) createTreeView(list); - logger.info("List here: " + ret); this.secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND, ret); } catch (HibernateException e) { throw new RuntimeException(e); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index cf5fa093e70..2e3dd9ece7b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,12 +407,13 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - List newChoices = (List)getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + List newChoices = (List)getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); if (newChoices!=null) { choices= newChoices; choices2= new HashSet<>(newChoices); } + logger.info("Choices here: " + choices); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { From cef4fb2870c58c21aa247884c3eeb2cdc4db23b5 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 18:18:08 +0300 Subject: [PATCH 64/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 17a102dd2b7..91939519b46 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -137,14 +137,17 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.updateBasedOnData(srcSectorSelected); + + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); -// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); target.add(secondarySectorsTable.getSearchSectors()); + secondarySectorsTable.refreshTable(target); } From f976798535d329e03d710469548c619251d0b124 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 18:32:37 +0300 Subject: [PATCH 65/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 91939519b46..530d0f10720 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -137,17 +137,17 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + primarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); - List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - secondarySectorsTable.updateBasedOnData(srcSectorSelected); - - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// +// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); target.add(secondarySectorsTable.getSearchSectors()); - secondarySectorsTable.refreshTable(target); + } From f72547872b7c0edf5cce3203cf11e12bd345980e Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 18:40:28 +0300 Subject: [PATCH 66/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 530d0f10720..b3c60f8fbbc 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -137,7 +137,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - primarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); // List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); From c6eb1f9e380ecab2a518007daf5d1468ae99857b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 18:44:18 +0300 Subject: [PATCH 67/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 2e3dd9ece7b..84589fd426e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,7 +407,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - List newChoices = (List)getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); + List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); if (newChoices!=null) { choices= newChoices; From db8418389bfa67667cf888c4632a2125b1e7bbe0 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 18:55:00 +0300 Subject: [PATCH 68/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index b3c60f8fbbc..3b888fd64e4 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -250,7 +250,7 @@ protected Collection load() { Restrictions.eq( "deleted", Boolean.FALSE) ))); - List srcSectorSelected = (List) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); Junction junction2 = null; if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { List ids = searchSectorsDstFromMapping(srcSectorSelected); From 419042072a63710a137510719c253d42d8e39d83 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 19:03:11 +0300 Subject: [PATCH 69/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 3b888fd64e4..81d62e2e03e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -139,11 +139,11 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); -// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); + List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +// + secondarySectorsTable.updateBasedOnData(srcSectorSelected); // -// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); target.add(secondarySectorsTable.getSearchSectors()); @@ -250,7 +250,7 @@ protected Collection load() { Restrictions.eq( "deleted", Boolean.FALSE) ))); - List srcSectorSelected = (List) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + List srcSectorSelected = (List) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); Junction junction2 = null; if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { List ids = searchSectorsDstFromMapping(srcSectorSelected); From e0e3039a49f176296d5419a54321f1d07fb9748e Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 19:20:40 +0300 Subject: [PATCH 70/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 81d62e2e03e..930a34ce16b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -146,7 +146,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - target.add(secondarySectorsTable.getSearchSectors()); + target.add(secondarySectorsTable); From fb278053b2b3834b9a92abbd9642a1fc54a10231 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Fri, 14 Jun 2024 19:37:42 +0300 Subject: [PATCH 71/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- .../components/features/tables/AmpSectorsFormTableFeature.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 930a34ce16b..17f2e0d3d1b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -116,7 +116,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { - List choices = (List)load(); + List choices = (List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); List sectorChoices = searchSectorsDstFromMapping(selectedSector); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 84589fd426e..298a1fb6056 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,7 +407,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); + List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); if (newChoices!=null) { choices= newChoices; From 32ee59195ee1b3a60b43f4beb53ae60c4100f544 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:03:35 +0300 Subject: [PATCH 72/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 17f2e0d3d1b..930a34ce16b 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -116,7 +116,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { - List choices = (List)secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); + List choices = (List)load(); List sectorChoices = searchSectorsDstFromMapping(selectedSector); From 799fc18e0607c8a5061e04de383148cd9f1662ad Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:05:10 +0300 Subject: [PATCH 73/85] AMP-30574 Map primary and secondary sectors --- .../components/features/tables/AmpSectorsFormTableFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 298a1fb6056..84589fd426e 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,7 +407,7 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND); + List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); if (newChoices!=null) { choices= newChoices; From 90e22863821890bcc34ed2acd64d8acf10b60e1d Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:18:52 +0300 Subject: [PATCH 74/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 930a34ce16b..a6d12bcf178 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -198,9 +198,6 @@ protected Collection createTreeView(Collec } - - - private List searchSectorsDstFromMapping(AmpSector srcSector) { Session session = PersistenceManager.getRequestDBSession(); List dstSectorIds = new ArrayList<>(); From 69b009afce9e4e50cc1334f5397ff3c739fd7bf7 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:28:08 +0300 Subject: [PATCH 75/85] AMP-30574 Map primary and secondary sectors --- amp/repository/aim/view/allVisibilityTags.jsp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/amp/repository/aim/view/allVisibilityTags.jsp b/amp/repository/aim/view/allVisibilityTags.jsp index 1f8c97e0117..8321e13fd7e 100644 --- a/amp/repository/aim/view/allVisibilityTags.jsp +++ b/amp/repository/aim/view/allVisibilityTags.jsp @@ -20,9 +20,9 @@ - + - + From 1a0a62a7fe8686c6d3f15c9b7b93fc8874d14acd Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:49:49 +0300 Subject: [PATCH 76/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index a6d12bcf178..310e096ab5c 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -135,18 +135,22 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.getSetModel().getObject().add(newSector); } target.add(secondarySectorsTable.getList().getParent()); + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); - } - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); - - List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); + List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); // - secondarySectorsTable.updateBasedOnData(srcSectorSelected); + secondarySectorsTable.updateBasedOnData(srcSectorSelected); // - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - target.add(secondarySectorsTable); + target.add(secondarySectorsTable); + } + else + { + return; + } + From 684c54e7de7e413a76e8a9c06d3cc178c2f65a18 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:50:11 +0300 Subject: [PATCH 77/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 310e096ab5c..1a709ceb39f 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -144,7 +144,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - target.add(secondarySectorsTable); + target.add(secondarySectorsTable.getSearchSectors()); } else { From 28b590514f26fcb19bd2e2c447e13022c2c3b292 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 01:51:38 +0300 Subject: [PATCH 78/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 1a709ceb39f..1323213bec6 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -135,13 +135,13 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.getSetModel().getObject().add(newSector); } target.add(secondarySectorsTable.getList().getParent()); - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); - - List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -// - secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); // - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); +// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +//// +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); +//// +// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); target.add(secondarySectorsTable.getSearchSectors()); From b23bc4d33e57ff47561740bc024afe3f783a58e3 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 02:01:50 +0300 Subject: [PATCH 79/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 1323213bec6..daf52044b9f 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -123,7 +123,7 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Choices found: " + choices); logger.info("Sector Choices found: " + sectorChoices); if (sectorChoices.size() == 1) { - for (AmpSector secondarySector : choices) { + for (AmpSector secondarySector : sectorChoices) { AmpActivitySector newSector = new AmpActivitySector(); newSector.setSectorId(secondarySector); newSector.setActivityId(secondarySectorsTable.getSetModel().getObject().iterator().next().getActivityId()); // Assuming activityId is the same @@ -135,22 +135,18 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla secondarySectorsTable.getSetModel().getObject().add(newSector); } target.add(secondarySectorsTable.getList().getParent()); -// secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); -// -// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -//// -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); -//// -// secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - - target.add(secondarySectorsTable.getSearchSectors()); - } - else - { - return; } + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); + List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +// + secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// + secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); + + target.add(secondarySectorsTable.getSearchSectors()); + target.add(secondarySectorsTable); From abfd07e2aa5d9c16fe6400568e0e5697a6a55c08 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 02:14:02 +0300 Subject: [PATCH 80/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index daf52044b9f..742b0b70168 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -116,11 +116,11 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { - List choices = (List)load(); +// List choices = (List)load(); List sectorChoices = searchSectorsDstFromMapping(selectedSector); - logger.info("Choices found: " + choices); +// logger.info("Choices found: " + choices); logger.info("Sector Choices found: " + sectorChoices); if (sectorChoices.size() == 1) { for (AmpSector secondarySector : sectorChoices) { @@ -137,8 +137,9 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, choices); - + else { + secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND)); + } List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); // secondarySectorsTable.updateBasedOnData(srcSectorSelected); From ce087e5ddb7419f2287d597eb4d0aa712e44c0c1 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 02:33:25 +0300 Subject: [PATCH 81/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 9 +++------ .../onepager/models/AmpSectorSearchModel.java | 1 + .../AMP-30574-Add-sectors-mapping-to-GFM.xml | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 amp/xmlpatches/general/AMP-30574-Add-sectors-mapping-to-GFM.xml diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 742b0b70168..d1d1ae7a3d2 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -116,11 +116,8 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { -// List choices = (List)load(); List sectorChoices = searchSectorsDstFromMapping(selectedSector); - -// logger.info("Choices found: " + choices); logger.info("Sector Choices found: " + sectorChoices); if (sectorChoices.size() == 1) { for (AmpSector secondarySector : sectorChoices) { @@ -140,9 +137,9 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla else { secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND)); } - List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -// - secondarySectorsTable.updateBasedOnData(srcSectorSelected); +// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); +//// +// secondarySectorsTable.updateBasedOnData(srcSectorSelected); // secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index 4f98d600f29..f83f02143ae 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -71,6 +71,7 @@ protected Collection load() { ))); List srcSectorSelected = (List) getParams().get(PARAM.SRC_SECTOR_SELECTED); + logger.info("SEctors selected: " + srcSectorSelected); Junction junction2 = null; if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { List ids = searchSectorsDstFromMapping(srcSectorSelected); diff --git a/amp/xmlpatches/general/AMP-30574-Add-sectors-mapping-to-GFM.xml b/amp/xmlpatches/general/AMP-30574-Add-sectors-mapping-to-GFM.xml new file mode 100644 index 00000000000..18ce6834f7a --- /dev/null +++ b/amp/xmlpatches/general/AMP-30574-Add-sectors-mapping-to-GFM.xml @@ -0,0 +1,20 @@ + + + AMP-23648 + Move FM editable exports to modules + vchihai + Activity editable exports in public view + + + + From 939a3bcf8611d5c087721cc862d1995aef8acc2b Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 02:59:53 +0300 Subject: [PATCH 82/85] AMP-30574 Map primary and secondary sectors --- .../amp/onepager/models/AmpSectorSearchModel.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index f83f02143ae..fda0652905a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -7,6 +7,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -18,6 +19,7 @@ import org.digijava.module.aim.util.AmpAutoCompleteDisplayable; import org.hibernate.Criteria; import org.hibernate.HibernateException; +import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.criterion.Junction; import org.hibernate.criterion.Order; @@ -109,6 +111,17 @@ private List searchSectorsDstFromMapping(List srcSectors) { session = PersistenceManager.getSession(); Criteria crit = session.createCriteria(AmpSectorMapping.class); crit.setCacheable(true); + Query query; + for (AmpSector sector : srcSectors) { + query =session.createQuery("FROM " + AmpSectorMapping.class.getName() + " am WHERE am.srcSector.ampSectorId = :sectorId"); + query.setParameter("sectorId", sector.getAmpSectorId()); + List list = query.list(); + if (list== null || list.isEmpty()) { + return Collections.emptyList(); + } + + } + if (srcSectors != null && !srcSectors.isEmpty()) { Junction junction = Restrictions.conjunction().add( From b93df8b742a8e30d19eb14d1016423ef5484b660 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 03:10:01 +0300 Subject: [PATCH 83/85] AMP-30574 Map primary and secondary sectors --- .../sections/AmpSectorsFormSectionFeature.java | 10 +--------- .../features/tables/AmpSectorsFormTableFeature.java | 7 ------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index d1d1ae7a3d2..194280ea4c8 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -134,16 +134,8 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla target.add(secondarySectorsTable.getList().getParent()); } - else { - secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.NEW_CHOICES, secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND)); - } -// List srcSectorSelected = (List) primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); -//// -// secondarySectorsTable.updateBasedOnData(srcSectorSelected); -// - secondarySectorsTable.triggerUpdateEvent(secondarySectorsTable.getSetModel().getObject(), sectorClassification); - target.add(secondarySectorsTable.getSearchSectors()); + target.add(secondarySectorsTable); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 84589fd426e..6c8dc1acd29 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -407,13 +407,6 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - List newChoices = (List)searchSectors.getModelParams().get(AmpSectorSearchModel.PARAM.NEW_CHOICES); - if (newChoices!=null) - { - choices= newChoices; - choices2= new HashSet<>(newChoices); - } - logger.info("Choices here: " + choices); if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { From acb7cc4518582f5991dc127e042e4d114e079ce4 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 03:19:32 +0300 Subject: [PATCH 84/85] AMP-30574 Map primary and secondary sectors --- .../AmpSectorsFormSectionFeature.java | 112 ------------------ .../tables/AmpSectorsFormTableFeature.java | 1 - .../onepager/models/AmpSectorSearchModel.java | 3 - 3 files changed, 116 deletions(-) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 194280ea4c8..38b29c6d036 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -113,12 +113,10 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla { AmpSector selectedSector =(AmpSector) this.primarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.CURRENT_SRC_SECTOR_SELECTED); - logger.info("Selected sector: " + selectedSector); try { if (selectedSector!=null) { List sectorChoices = searchSectorsDstFromMapping(selectedSector); - logger.info("Sector Choices found: " + sectorChoices); if (sectorChoices.size() == 1) { for (AmpSector secondarySector : sectorChoices) { AmpActivitySector newSector = new AmpActivitySector(); @@ -151,42 +149,6 @@ private void populateSecondarySectorsFor1Choice(AjaxRequestTarget target, AmpCla } - protected void addToRootTree(Collection tree, AmpAutoCompleteDisplayable obj, - boolean searchHit) { - if (obj.getParent() == null) - tree.add(obj); - else { - if (searchHit) - obj.getVisibleSiblings().addAll(obj.getNonDeletedChildren()); - obj.getParent().getVisibleSiblings().add(obj); - addToRootTree(tree, obj.getParent(), false); - } - } - - protected void addToRootList(Collection list, AmpAutoCompleteDisplayable obj) { - list.add(obj); - Collection children = obj.getVisibleSiblings(); - for (AmpAutoCompleteDisplayable ampSector : children) { - addToRootList(list, ampSector); - } - } - - protected Collection createTreeView(Collection l) { - Boolean b = (Boolean) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AbstractAmpAutoCompleteModel.PARAM.EXACT_MATCH); - if (b != null && b) - return l; - Collection ret = new ArrayList(); - - Set root = new TreeSet(new AmpAutoCompleteDisplayable.AmpAutoCompleteComparator()); - for (Object o : l) - addToRootTree(root, (AmpAutoCompleteDisplayable) o, true); - for (Object o : root) { - addToRootList(ret, (AmpAutoCompleteDisplayable) o); - } - - return ret; - } - private List searchSectorsDstFromMapping(AmpSector srcSector) { Session session = PersistenceManager.getRequestDBSession(); @@ -219,82 +181,8 @@ private List searchSectorsDstFromMapping(AmpSector srcSector) { } - protected Collection load() { - Collection ret= new ArrayList<>(); - Session session = PersistenceManager.getSession(); - try { - session.enableFilter("isDeletedFilter").setParameter("deleted", Boolean.FALSE); - - Integer maxResults = (Integer) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AbstractAmpAutoCompleteModel.PARAM.MAX_RESULTS); - AmpSectorScheme scheme = (AmpSectorScheme) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SECTOR_SCHEME); - Criteria crit = session.createCriteria(AmpSector.class); - crit.setCacheable(true); - Junction junction = Restrictions.conjunction().add( - Restrictions.and( - Restrictions.eq("ampSecSchemeId", scheme), - Restrictions.or( - Restrictions.isNull("deleted"), - Restrictions.eq( "deleted", Boolean.FALSE) - ))); - - List srcSectorSelected = (List) this.secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.SRC_SECTOR_SELECTED); - Junction junction2 = null; - if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { - List ids = searchSectorsDstFromMapping(srcSectorSelected); - if (!ids.isEmpty()) { - junction2 = Restrictions.conjunction().add( - Restrictions.in("ampSectorId", ids)); - } - } - - crit.add(junction); - if (junction2 != null) crit.add(junction2); - crit.addOrder(Order.asc("name")); - if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); - List list = crit.list(); - ret = (Collection) createTreeView(list); - this.secondarySectorsTable.getSearchSectors().getModelParams().put(AmpSectorSearchModel.PARAM.DST_SECTORS_FOUND, ret); - } catch (HibernateException e) { - throw new RuntimeException(e); - } finally { - session.disableFilter("isDeletedFilter"); - } - return ret; - } - - /* - * Search for sectors that are mapped to the given sector - * */ - private List searchSectorsDstFromMapping(List srcSectors) { - List ids = new ArrayList(); - Session session = PersistenceManager.getRequestDBSession(); - try { - Criteria crit = session.createCriteria(AmpSectorMapping.class); - crit.setCacheable(true); - - if (srcSectors != null && !srcSectors.isEmpty()) { - Junction junction = Restrictions.conjunction().add( - Restrictions.in("srcSector", srcSectors) - ); - - crit.add(junction); - List list = crit.list(); - if (list != null && !list.isEmpty()) { - for (AmpSectorMapping mapping : list) { - ids.add(mapping.getDstSector().getAmpSectorId()); - } - } - } - } catch (HibernateException e) { - throw new RuntimeException(e); - } finally { - session.disableFilter("isDeletedFilter"); - } - return ids; - } - /** * Updates the user interface when a deletion event is triggered. * This method is called when the onDelete method is invoked in the containing class. diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java index 6c8dc1acd29..54e94e6e752 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/tables/AmpSectorsFormTableFeature.java @@ -408,7 +408,6 @@ public Collection getChoices(String input) { Collection choices = super.getChoices(input); Set choices2 = new HashSet<>(choices); - if (sectorClassification.getName().equals(AmpClassificationConfiguration.SECONDARY_CLASSIFICATION_CONFIGURATION_NAME)) { List selectedSectors = (List) this.getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED); diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java index fda0652905a..0cfcff90eb4 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/models/AmpSectorSearchModel.java @@ -73,7 +73,6 @@ protected Collection load() { ))); List srcSectorSelected = (List) getParams().get(PARAM.SRC_SECTOR_SELECTED); - logger.info("SEctors selected: " + srcSectorSelected); Junction junction2 = null; if (srcSectorSelected != null && !srcSectorSelected.isEmpty()) { List ids = searchSectorsDstFromMapping(srcSectorSelected); @@ -89,10 +88,8 @@ protected Collection load() { if (maxResults != null && maxResults != 0) crit.setMaxResults(maxResults); List list = crit.list(); - logger.info("List here 1: " + list); ret = (Collection) createTreeView(list); - logger.info("List here: " + ret); getParams().put(PARAM.DST_SECTORS_FOUND, ret); } catch (HibernateException e) { throw new RuntimeException(e); From b4202f23836be4b6b361ca9c986bbf9343cf7035 Mon Sep 17 00:00:00 2001 From: brianbrix Date: Sun, 16 Jun 2024 09:23:33 +0300 Subject: [PATCH 85/85] AMP-30574 Map primary and secondary sectors --- .../features/sections/AmpSectorsFormSectionFeature.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java index 38b29c6d036..6ae7562565a 100644 --- a/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java +++ b/amp/WEB-INF/src/org/dgfoundation/amp/onepager/components/features/sections/AmpSectorsFormSectionFeature.java @@ -76,6 +76,14 @@ public AmpSectorsFormSectionFeature(String id, String fmName,final IModel alreadyslectedSecSectors = secondarySectorsTable.getSetModel().getObject(); + if (alreadyslectedSecSectors!=null) { + for (AmpActivitySector ampActivitySector: alreadyslectedSecSectors) { + secondarySectorsTable.getSearchSectors().getModelParams().computeIfAbsent(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED, + k -> new ArrayList<>()); + ((List) secondarySectorsTable.getSearchSectors().getModelParams().get(AmpSectorSearchModel.PARAM.DST_SECTOR_SELECTED)).add(ampActivitySector); + } + } view.add(primarySectorsTable);