pending orgs management

This commit is contained in:
Michele Artini 2020-10-05 12:16:49 +02:00
parent 6b200d7a77
commit 5b10c7cbf4
21 changed files with 235 additions and 63 deletions

View File

@ -78,7 +78,7 @@ public class OrganizationController {
} else if (StringUtils.isBlank(org.getType())) { } else if (StringUtils.isBlank(org.getType())) {
throw new RuntimeException("Missing field: type"); throw new RuntimeException("Missing field: type");
} else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) { } else if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(org.getCountry(), authentication.getName())) {
final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName(), StringUtils.isNotBlank(org.getId())); final String orgId = databaseUtils.insertOrUpdateOrganization(org, authentication.getName());
return Arrays.asList(orgId); return Arrays.asList(orgId);
} else { } else {
throw new RuntimeException("User not authorized"); throw new RuntimeException("User not authorized");
@ -225,6 +225,23 @@ public class OrganizationController {
} }
} }
@GetMapping("/byCountry/{status}/{code}")
public Iterable<OrganizationSimpleView> findPendingOrgsByCountry(@PathVariable final String status,
@PathVariable final String code,
final Authentication authentication) {
if (UserInfo.isSuperAdmin(authentication) || userCountryRepository.verifyAuthorizationForCountry(code, authentication.getName())) {
if (status.equalsIgnoreCase("approved")) {
return organizationSimpleViewRepository.findByCountryAndApprovedOrderByName(code, true);
} else if (status.equalsIgnoreCase("pending")) {
return organizationSimpleViewRepository.findByCountryAndApprovedOrderByName(code, false);
} else {
return organizationSimpleViewRepository.findByCountryOrderByName(code);
}
} else {
throw new RuntimeException("User not authorized");
}
}
@GetMapping("/byType/{type}/{page}/{size}") @GetMapping("/byType/{type}/{page}/{size}")
public Page<OrganizationSimpleView> findByType(@PathVariable final String type, public Page<OrganizationSimpleView> findByType(@PathVariable final String type,
@PathVariable final int page, @PathVariable final int page,

View File

@ -13,6 +13,8 @@ public class SuggestionInfo {
private long nConflicts = 0; private long nConflicts = 0;
private long nPendingOrgs = 0;
public long getnDuplicates() { public long getnDuplicates() {
return nDuplicates; return nDuplicates;
} }
@ -29,10 +31,20 @@ public class SuggestionInfo {
this.nConflicts = nConflicts; this.nConflicts = nConflicts;
} }
public long getnPendingOrgs() {
return nPendingOrgs;
}
public void setnPenfingOrgs(final long nPendingOrgs) {
this.nPendingOrgs = nPendingOrgs;
}
public void add(final SuggestionInfoViewByCountry infoCountry) { public void add(final SuggestionInfoViewByCountry infoCountry) {
nDuplicates += infoCountry.getnDuplicates(); nDuplicates += infoCountry.getnDuplicates();
nConflicts += infoCountry.getnConflicts(); nConflicts += infoCountry.getnConflicts();
nPendingOrgs += infoCountry.getnPendingOrgs();
} }
} }
public SuggestionCounter total = new SuggestionCounter();; public SuggestionCounter total = new SuggestionCounter();;

View File

@ -18,8 +18,8 @@ import com.vladmihalcea.hibernate.type.json.JsonStringType;
@Entity @Entity
@Table(name = "organizations_view") @Table(name = "organizations_view")
@TypeDefs({ @TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class), @TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
}) })
public class OrganizationView implements Serializable { public class OrganizationView implements Serializable {
@ -70,6 +70,9 @@ public class OrganizationView implements Serializable {
@Column(name = "relations", columnDefinition = "jsonb") @Column(name = "relations", columnDefinition = "jsonb")
private Set<RelationByOrg> relations; private Set<RelationByOrg> relations;
@Column(name = "approved")
private boolean approved = false;
public String getId() { public String getId() {
return id; return id;
} }
@ -166,4 +169,12 @@ public class OrganizationView implements Serializable {
this.relations = relations; this.relations = relations;
} }
public boolean isApproved() {
return approved;
}
public void setApproved(final boolean approved) {
this.approved = approved;
}
} }

View File

@ -26,6 +26,9 @@ public class SuggestionInfoViewByCountry implements Serializable {
@Column(name = "n_conflicts") @Column(name = "n_conflicts")
private long nConflicts; private long nConflicts;
@Column(name = "n_pending_orgs")
private long nPendingOrgs;
public String getCountry() { public String getCountry() {
return country; return country;
} }
@ -50,4 +53,12 @@ public class SuggestionInfoViewByCountry implements Serializable {
this.nConflicts = nConflicts; this.nConflicts = nConflicts;
} }
public long getnPendingOrgs() {
return nPendingOrgs;
}
public void setnPendingOrgs(final long nPendingOrgs) {
this.nPendingOrgs = nPendingOrgs;
}
} }

View File

@ -21,6 +21,10 @@ public interface OrganizationSimpleViewRepository extends ReadOnlyRepository<Org
Page<OrganizationSimpleView> findByCountryOrderByName(String country, Pageable pageable); Page<OrganizationSimpleView> findByCountryOrderByName(String country, Pageable pageable);
Iterable<OrganizationSimpleView> findByCountryOrderByName(String code);
Iterable<OrganizationSimpleView> findByCountryAndApprovedOrderByName(String code, boolean approved);
Page<OrganizationSimpleView> findByTypeOrderByName(String type, Pageable pageable); Page<OrganizationSimpleView> findByTypeOrderByName(String type, Pageable pageable);
@Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where uc.email = ?2 and o.type = ?1 order by o.name", nativeQuery = true) @Query(value = "select o.* from organizations_simple_view o left outer join user_countries uc on (uc.country = o.country) where uc.email = ?2 and o.type = ?1 order by o.name", nativeQuery = true)

View File

@ -15,6 +15,7 @@ import java.util.stream.Collectors;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -89,12 +90,20 @@ public class DatabaseUtils {
} }
@Transactional @Transactional
public String insertOrUpdateOrganization(final OrganizationView orgView, final String user, final boolean update) { public String insertOrUpdateOrganization(final OrganizationView orgView, final String user) {
final boolean update = StringUtils.isNotBlank(orgView.getId());
if (update) { if (update) {
cleanOldRelations(orgView.getId()); cleanOldRelations(orgView.getId());
} }
if (!orgView.isApproved()) {
cleanOldRelations(orgView.getId());
organizationRepository.deleteById(orgView.getId());
orgView.setId(null);
}
final Organization org = new Organization(update ? orgView.getId() : null, final Organization org = new Organization(update ? orgView.getId() : null,
orgView.getName(), orgView.getName(),
orgView.getType(), orgView.getType(),

View File

@ -1,8 +1,8 @@
spring.main.banner-mode = off spring.main.banner-mode = off
logging.level.root = INFO logging.level.root = INFO
spring.datasource.url=jdbc:postgresql://localhost:5432/dnet_orgs spring.datasource.url=jdbc:postgresql://localhost:5432/oa_organizations
spring.datasource.username= spring.datasource.username=oa_organizations
spring.datasource.password= spring.datasource.password=
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

View File

@ -165,6 +165,7 @@ CREATE VIEW organizations_view AS SELECT
org.lng, org.lng,
org.city, org.city,
org.country, org.country,
org.approved,
COALESCE(jsonb_agg(DISTINCT jsonb_build_object('id', oid.otherid, 'type', oid.type)) FILTER (WHERE oid.otherid IS NOT NULL), '[]') AS other_ids, COALESCE(jsonb_agg(DISTINCT jsonb_build_object('id', oid.otherid, 'type', oid.type)) FILTER (WHERE oid.otherid IS NOT NULL), '[]') AS other_ids,
COALESCE(jsonb_agg(DISTINCT jsonb_build_object('name', n.name, 'lang', n.lang)) FILTER (WHERE n.name IS NOT NULL), '[]') AS other_names, COALESCE(jsonb_agg(DISTINCT jsonb_build_object('name', n.name, 'lang', n.lang)) FILTER (WHERE n.name IS NOT NULL), '[]') AS other_names,
COALESCE(jsonb_agg(DISTINCT a.acronym) FILTER (WHERE a.acronym IS NOT NULL), '[]') AS acronyms, COALESCE(jsonb_agg(DISTINCT a.acronym) FILTER (WHERE a.acronym IS NOT NULL), '[]') AS acronyms,
@ -185,7 +186,8 @@ GROUP BY
org.lat, org.lat,
org.lng, org.lng,
org.city, org.city,
org.country; org.country,
org.approved;
CREATE VIEW organizations_info_view AS SELECT CREATE VIEW organizations_info_view AS SELECT
org.id, org.id,
@ -232,11 +234,13 @@ GROUP BY u.email, u.valid, u.role
ORDER BY u.email; ORDER BY u.email;
CREATE VIEW suggestions_info_by_country_view AS SELECT c.val AS country, CREATE VIEW suggestions_info_by_country_view AS SELECT c.val AS country,
coalesce(t1.n_duplicates, 0) AS n_duplicates, coalesce(t1.n_duplicates, 0) AS n_duplicates,
coalesce(t2.n_conflicts, 0) AS n_conflicts coalesce(t2.n_conflicts, 0) AS n_conflicts,
coalesce(t3.n_pending_orgs, 0) AS n_pending_orgs
FROM countries c FROM countries c
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' GROUP BY o.country) AS t1 ON (t1.country = c.val) LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT d.*) AS n_duplicates FROM oa_duplicates d LEFT OUTER JOIN organizations o ON (d.local_id = o.id) WHERE d.reltype = 'suggested' GROUP BY o.country) AS t1 ON (t1.country = c.val)
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' GROUP BY o.country) AS t2 ON (t2.country = c.val); LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT c.idgroup) AS n_conflicts FROM oa_conflicts c LEFT OUTER JOIN organizations o ON (c.id1 = o.id) WHERE c.reltype = 'suggested' GROUP BY o.country) AS t2 ON (t2.country = c.val)
LEFT OUTER JOIN (SELECT o.country AS country, count(DISTINCT o.id) AS n_pending_orgs FROM organizations o WHERE o.approved = false GROUP BY o.country) AS t3 ON (t3.country = c.val);
CREATE VIEW conflict_groups_view AS SELECT CREATE VIEW conflict_groups_view AS SELECT
c.idgroup AS idgroup, c.idgroup AS idgroup,

View File

@ -9,6 +9,9 @@
<div class="card"> <div class="card">
<org-tabs-menu org-id="{{orgId}}" info="info" org="org" events="events" selected="currentTab"></org-tabs-menu> <org-tabs-menu org-id="{{orgId}}" info="info" org="org" events="events" selected="currentTab"></org-tabs-menu>
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1"></org-form-metadata>
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1 && org.approved" mode="update"></org-form-metadata>
<org-form-metadata org-id="{{orgId}}" org="org" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 1 && !org.approved" mode="approve"></org-form-metadata>
<org-dedup-events org-id="{{orgId}}" events="events" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 2"></org-dedup-events> <org-dedup-events org-id="{{orgId}}" events="events" vocabularies="vocabularies" info-method="getInfo()" ng-if="currentTab == 2"></org-dedup-events>
</div> </div>

View File

@ -2,10 +2,10 @@
<div class="text-muted" ng-if="conflicts.length == 0">No suggestions</div> <div class="text-muted" ng-if="conflicts.length == 0">No suggestions</div>
<div class="input-group input-group-sm mb-3" ng-if="conflicts.length > 0"> <div class="input-group input-group-sm mb-3" ng-show="conflicts.length > 0">
<input type="text" class="form-control" ng-model="conflictFilter" placeholder="Filter..."> <input type="text" class="form-control" ng-model="conflictFilter" placeholder="Filter...">
<div class="input-group-append"> <div class="input-group-append">
<span class="input-group-text text-outline-primary" id="inputGroup-sizing-sm">Country:</span> <span class="input-group-text text-outline-primary">Country:</span>
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button> <button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<small> <small>

View File

@ -2,10 +2,10 @@
<div class="text-muted" ng-if="duplicates.length == 0">No suggestions</div> <div class="text-muted" ng-if="duplicates.length == 0">No suggestions</div>
<div class="input-group input-group-sm mb-3" ng-if="duplicates.length > 0"> <div class="input-group input-group-sm mb-3" ng-show="duplicates.length > 0">
<input type="text" class="form-control" ng-model="duplicateFilter" placeholder="Filter..."> <input type="text" class="form-control" ng-model="duplicateFilter" placeholder="Filter...">
<div class="input-group-append"> <div class="input-group-append">
<span class="input-group-text text-outline-primary" id="inputGroup-sizing-sm">Country:</span> <span class="input-group-text text-outline-primary">Country:</span>
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button> <button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<small> <small>

View File

@ -1,24 +1,29 @@
<div class="card-body"> <div class="card-body">
<form name="organizationForm"> <form name="organizationForm">
<div ng-if="mode == 'approve'" class="alert alert-warning">
This is a pending organization. Please evaluate it before approving.
</div>
<fieldset> <fieldset>
<legend>Official name and type</legend> <legend>Official name and type</legend>
<div class="form-group"> <div class="form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text bg-primary text-white">name</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">name</div>
</div> </div>
<input name="org_nm" type="text" class="form-control" <input name="org_nm" type="text" class="form-control"
placeholder="organization name..." ng-model="org.name" placeholder="organization name..." ng-model="org.name"
required="required" required="required"
ng-class="{'is-invalid' : organizationForm.org_nm.$error.required}" /> ng-class="{'is-invalid' : organizationForm.org_nm.$error.required}" />
<div class="input-group-append input-group-prepend"> <div class="input-group-append input-group-prepend">
<div class="input-group-text bg-primary text-white">type</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">type</div>
</div> </div>
<select name="org_tp" class="custom-select" ng-model="org.type" <select name="org_tp" class="custom-select" ng-model="org.type"
required="required" required="required"
ng-class="{'is-invalid' : organizationForm.org_tp.$error.required}"> ng-class="{'is-invalid' : organizationForm.org_tp.$error.required}">
<option disabled="disabled" value=''>type...</option> <option disabled="disabled" value='' ng-if="!org.type">type...</option>
<option ng-repeat="t in vocabularies.orgTypes">{{t}}</option> <option ng-repeat="t in vocabularies.orgTypes">{{t}}</option>
</select> </select>
</div> </div>
@ -30,26 +35,27 @@
<div class="form-group"> <div class="form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<div class="input-group-text bg-primary text-white">city</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">city</div>
</div> </div>
<input name="org_ct" type="text" class="form-control" placeholder="" <input name="org_ct" type="text" class="form-control" placeholder=""
ng-model="org.city" /> ng-model="org.city" />
<div class="input-group-append input-group-prepend"> <div class="input-group-append input-group-prepend">
<div class="input-group-text bg-primary text-white">country</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">country</div>
</div> </div>
<select name="org_cntr" class="custom-select" ng-model="org.country" <select name="org_cntr" class="custom-select" ng-model="org.country"
required="required" required="required"
ng-class="{'is-invalid' : organizationForm.org_cntr.$error.required}"> ng-class="{'is-invalid' : organizationForm.org_cntr.$error.required}">
<option disabled="disabled" value=''>country...</option> <option disabled="disabled" value='' ng-if="!org.country">country...</option>
<option ng-repeat="c in vocabularies.countries">{{c}}</option> <option ng-repeat="c in vocabularies.countries">{{c}}</option>
</select> </select>
<div class="input-group-append input-group-prepend"> <div class="input-group-append input-group-prepend">
<div class="input-group-text bg-primary text-white">lat</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">lat</div>
</div> </div>
<input name="org_lat" type="text" class="form-control" <input name="org_lat" type="text" class="form-control"
placeholder="0.0" ng-model="org.lat" /> placeholder="0.0" ng-model="org.lat" />
<div class="input-group-append input-group-prepend"> <div class="input-group-append input-group-prepend">
<div class="input-group-text bg-primary text-white">lng</div> <div class="input-group-text text-white" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">lng</div>
</div> </div>
<input name="org_lng" type="text" class="form-control" <input name="org_lng" type="text" class="form-control"
placeholder="0.0" ng-model="org.lng" /> placeholder="0.0" ng-model="org.lng" />
@ -63,7 +69,7 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-lg-8 mb-2"> <div class="col-lg-8 mb-2">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white text-center py-1">Acronyms</div> <div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Acronyms</div>
<table class="table table-sm"> <table class="table table-sm">
<tbody> <tbody>
<tr ng-repeat="a in org.acronyms"> <tr ng-repeat="a in org.acronyms">
@ -97,7 +103,7 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-lg-8 mb-2"> <div class="col-lg-8 mb-2">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white text-center py-1">Aliases</div> <div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Aliases</div>
<table class="table table-sm"> <table class="table table-sm">
<thead class="thead-light"> <thead class="thead-light">
<tr> <tr>
@ -144,7 +150,7 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-lg-8 mb-2"> <div class="col-lg-8 mb-2">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white text-center py-1">Identifiers</div> <div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Identifiers</div>
<table class="table table-sm"> <table class="table table-sm">
<thead class="thead-light"> <thead class="thead-light">
<tr> <tr>
@ -191,7 +197,7 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-lg-8 mb-2"> <div class="col-lg-8 mb-2">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white text-center py-1">Urls</div> <div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Urls</div>
<table class="table table-sm"> <table class="table table-sm">
<tbody> <tbody>
<tr ng-repeat="u in org.urls"> <tr ng-repeat="u in org.urls">
@ -228,7 +234,7 @@
<div class="form-group row"> <div class="form-group row">
<div class="col-lg-8 mb-2"> <div class="col-lg-8 mb-2">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white text-center py-1">Relations</div> <div class="card-header text-white text-center py-1" ng-class="{'bg-primary' : mode != 'approve', 'bg-warning' : mode == 'approve'}">Relations</div>
<table class="table table-sm"> <table class="table table-sm">
<thead class="thead-light"> <thead class="thead-light">
<tr> <tr>
@ -274,7 +280,10 @@
</div> </div>
</fieldset> </fieldset>
<button type="submit" class="btn btn-primary" ng-click="save()" ng-disabled="organizationForm.$invalid">save</button> <button type="submit" class="btn" ng-class="{'btn-primary' : mode != 'approve', 'btn-warning' : mode == 'approve'}" ng-click="save()" ng-disabled="organizationForm.$invalid">
<span ng-if="mode != 'approve'">Save</span>
<span ng-if="mode == 'approve'">Register as new Organization</span>
</button>
</form> </form>
</div> </div>

View File

@ -0,0 +1,43 @@
<div>
<div class="text-muted" ng-if="orgs.length == 0">No suggestions</div>
<div class="input-group input-group-sm mb-3" ng-show="orgs.length > 0">
<input type="text" class="form-control" ng-model="orgFilter" placeholder="Filter..."/>
<div class="input-group-append">
<span class="input-group-text text-outline-primary">Country:</span>
<button class="btn btn-outline-primary dropdown-toggle" data-toggle="dropdown">{{country}}</button>
<div class="dropdown-menu">
<small>
<a class="dropdown-item" href="#!/suggestions/{{c}}/0"
ng-repeat="(c, vals) in info.byCountry"
ng-if="vals.nPendingOrgs > 0">
{{c}} <span class="badge badge-primary float-right">{{vals.nPendingOrgs}}</span>
</a>
</small>
</div>
</div>
</div>
<table class="table table-sm table-hover" ng-if="orgs.length > 0">
<thead class="thead-light">
<tr class="d-flex">
<th class="col-6">Organization name</th>
<th class="col-4">Place</th>
<th class="col-1 text-center">Acronyms</th>
<th class="col-1 text-right">Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="o in orgs | filter:orgFilter" class="d-flex">
<td class="col-6">
<a href="#!/edit/0/{{o.id}}" title="{{o.id}}">{{o.name}}</a>
</td>
<td class="col-4"><img ng-src="resources/images/flags/{{o.country}}.gif" /> {{o.city}}, {{o.country}}</td>
<td class="col-1 text-center">{{o.acronyms.join()}}</td>
<td class="col-1 text-right">{{o.type}}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -13,12 +13,11 @@
</div> </div>
</div> </div>
<div ng-show="searchValue"> <div ng-show="searchValue">
<org-results-page orgs="searchOrgs"
<org-results orgs="searchOrgs"
prev-function="search(searchValue, searchOrgs.number - 1, searchOrgs.size)" prev-function="search(searchValue, searchOrgs.number - 1, searchOrgs.size)"
next-function="search(searchValue, searchOrgs.number + 1, searchOrgs.size)" next-function="search(searchValue, searchOrgs.number + 1, searchOrgs.size)"
selected-org="selectedOrg" selected-org="selectedOrg"
mode="select-modal"></org-results> mode="select-modal"></org-results-page>
</div> </div>
</div> </div>

View File

@ -1,7 +1,19 @@
<p ng-if="mode != 'select-modal'"> <p ng-if="mode != 'select-modal'">
<span ng-if="orgs.totalElements > 0">Page: {{orgs.number + 1}} / {{orgs.totalPages}}<br />Total elements: {{orgs.totalElements}}<br />{{searchMessage}}</span> <span ng-if="orgs.totalElements > 0">
<span ng-if="orgs.totalElements == 0">Page: -<br />Total elements: 0<br />{{searchMessage}}</span> Page: {{orgs.number + 1}} / {{orgs.totalPages}}<br />
<span ng-if="!(orgs.totalElements || orgs.totalElements === 0)">Page:<br />Total elements:<br />{{searchMessage}}</span> Total elements: {{orgs.totalElements}}<br />
{{searchMessage}}
</span>
<span ng-if="orgs.totalElements == 0">
Page: -<br />
Total elements: 0<br />
{{searchMessage}}
</span>
<span ng-if="!(orgs.totalElements || orgs.totalElements === 0)">
Page:<br />
Total elements:<br />
{{searchMessage}}
</span>
</p> </p>
<p ng-if="mode == 'select-modal'" class="text-right"> <p ng-if="mode == 'select-modal'" class="text-right">
@ -14,6 +26,7 @@
</h4> </h4>
<div ng-if="orgs.totalElements > 0"> <div ng-if="orgs.totalElements > 0">
<nav> <nav>
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
<li class="page-item" ng-class="{'disabled' : orgs.first }"> <li class="page-item" ng-class="{'disabled' : orgs.first }">
@ -25,7 +38,7 @@
</ul> </ul>
</nav> </nav>
<table class="table table-sm table-hover"> <table class="table table-sm table-hover" ng-if="orgs.content.length > 0">
<thead class="thead-light"> <thead class="thead-light">
<tr class="d-flex"> <tr class="d-flex">
<th class="col-6">Organization name</th> <th class="col-6">Organization name</th>
@ -47,4 +60,6 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -1,4 +1,4 @@
<org-results search-message="Searching for country: {{fieldValue}}" <org-results-page search-message="Searching for country: {{fieldValue}}"
orgs="orgs" orgs="orgs"
prev-function="prev()" prev-function="prev()"
next-function="next()"></org-results> next-function="next()"></org-results-page>

View File

@ -1,4 +1,4 @@
<org-results search-message="Searching for type: {{fieldValue}}" <org-results-page search-message="Searching for type: {{fieldValue}}"
orgs="orgs" orgs="orgs"
prev-function="prev()" prev-function="prev()"
next-function="next()"></org-results> next-function="next()"></org-results-page>

View File

@ -1,4 +1,4 @@
<org-results search-message="Searching for: {{searchText}}" <org-results-page search-message="Searching for: {{searchText}}"
orgs="orgs" orgs="orgs"
prev-function="prev()" prev-function="prev()"
next-function="next()"></org-results> next-function="next()"></org-results-page>

View File

@ -2,6 +2,9 @@
<div class="card-header"> <div class="card-header">
<ul class="nav nav-tabs card-header-tabs"> <ul class="nav nav-tabs card-header-tabs">
<li class="nav-item text-nowrap">
<a href="#!/suggestions/_/0" class="nav-link" ng-class="{'active': currentTab == 0}">Pending Organizations <span class="badge badge-primary ml-2">{{info.total.nPendingOrgs}}</span></a>
</li>
<li class="nav-item text-nowrap"> <li class="nav-item text-nowrap">
<a href="#!/suggestions/_/1" class="nav-link" ng-class="{'active': currentTab == 1}">New Duplicates <span class="badge badge-primary ml-2">{{info.total.nDuplicates}}</span></a> <a href="#!/suggestions/_/1" class="nav-link" ng-class="{'active': currentTab == 1}">New Duplicates <span class="badge badge-primary ml-2">{{info.total.nDuplicates}}</span></a>
</li> </li>
@ -12,6 +15,7 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<pending-orgs orgs="pendingOrgs" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 0"></pending-orgs>
<all-duplicates duplicates="duplicates" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 1"></all-duplicates> <all-duplicates duplicates="duplicates" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 1"></all-duplicates>
<all-conflicts conflicts="conflicts" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 2"></all-conflicts> <all-conflicts conflicts="conflicts" country="{{country}}" info="info" info-method="getInfo()" ng-if="currentTab == 2"></all-conflicts>
</div> </div>

View File

@ -146,7 +146,7 @@ orgsModule.directive('orgFormMetadata', function($http, $location, $route, $rout
'orgId' : '@', 'orgId' : '@',
'org' : '=', 'org' : '=',
'vocabularies' : '=', 'vocabularies' : '=',
'mode' : '@', // insert or update 'mode' : '@', // insert, update or approve
'infoMethod' : '&' 'infoMethod' : '&'
}, },
templateUrl: 'resources/html/forms/org_metadata.html', templateUrl: 'resources/html/forms/org_metadata.html',
@ -174,6 +174,7 @@ orgsModule.directive('orgFormMetadata', function($http, $location, $route, $rout
$http.post('api/organizations/save', scope.org).then(function successCallback(res) { $http.post('api/organizations/save', scope.org).then(function successCallback(res) {
if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } if ((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
else if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); } else if (scope.mode == 'insert') { $location.url('/edit/1/' + res.data[0]); }
else if (scope.mode == 'approve') { $location.url('/edit/3/' + res.data[0]); }
else if ($routeParams.msg == 2) { $route.reload(); } else if ($routeParams.msg == 2) { $route.reload(); }
else { $location.url('/edit/2/' + res.data[0]); } else { $location.url('/edit/2/' + res.data[0]); }
}, function errorCallback(res) { }, function errorCallback(res) {
@ -238,7 +239,7 @@ orgsModule.directive('orgDetails', function($http, $location, $route) {
} }
}); });
orgsModule.directive('orgResults', function($http, $location, $route) { orgsModule.directive('orgResultsPage', function($http, $location, $route) {
return { return {
restrict: 'E', restrict: 'E',
scope: { scope: {
@ -249,16 +250,21 @@ orgsModule.directive('orgResults', function($http, $location, $route) {
'selectedOrg' : '=', 'selectedOrg' : '=',
'mode' : '@' 'mode' : '@'
}, },
templateUrl: 'resources/html/parts/org_results.html', templateUrl: 'resources/html/parts/org_results_page.html',
link: function(scope, element, attrs, ctrl) { link: function(scope, element, attrs, ctrl) {
scope.selectOrg = function(o) { scope.selectOrg = function(o) {
scope.selectedOrg = o; scope.selectedOrg.id = o.id;
scope.selectedOrg.name = o.name;
scope.selectedOrg.type = o.name;
scope.selectedOrg.city = o.name;
scope.selectedOrg.country = o.name;
scope.selectedOrg.acronyms = o.name;
scope.selectedOrg.approved = o.name;
} }
} }
} }
}); });
orgsModule.directive('allConflicts', function($http, $location, $route, $q) { orgsModule.directive('allConflicts', function($http, $location, $route, $q) {
return { return {
restrict: 'E', restrict: 'E',
@ -288,6 +294,23 @@ orgsModule.directive('allConflicts', function($http, $location, $route, $q) {
} }
}); });
orgsModule.directive('pendingOrgs', function($http, $location, $route, $q) {
return {
restrict: 'E',
scope: {
'orgs' : '=',
'country' : '@',
'info' : '=',
'infoMethod' : '&'
},
templateUrl: 'resources/html/forms/pending_orgs.html',
link: function(scope, element, attrs, ctrl) {
}
}
});
orgsModule.directive('orgFormDuplicates', function($http, $location, $route) { orgsModule.directive('orgFormDuplicates', function($http, $location, $route) {
return { return {
restrict: 'E', restrict: 'E',
@ -407,7 +430,7 @@ orgsModule.config(function($routeProvider) {
.when('/edit/:msg/:id', { templateUrl: 'resources/html/edit.html', controller: 'showEditCtrl' }) .when('/edit/:msg/:id', { templateUrl: 'resources/html/edit.html', controller: 'showEditCtrl' })
.when('/suggestions/:country/:tab', { templateUrl: 'resources/html/suggestions.html', controller: 'showSuggestionsCtrl' }) .when('/suggestions/:country/:tab', { templateUrl: 'resources/html/suggestions.html', controller: 'showSuggestionsCtrl' })
.when('/users', { templateUrl: 'resources/html/users.html', controller: 'usersCtrl' }) .when('/users', { templateUrl: 'resources/html/users.html', controller: 'usersCtrl' })
.otherwise({ redirectTo: '/suggestions/_/1' }); .otherwise({ redirectTo: '/suggestions/_/0' });
}); });
orgsModule.filter('escape', function() { orgsModule.filter('escape', function() {
@ -559,7 +582,7 @@ orgsModule.controller('byTypeCtrl', function ($scope, $http, $routeParams, $loca
}); });
orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $route, $location, $timeout, vocabulariesService) { orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $route, $location, $timeout, $window, vocabulariesService) {
$scope.orgId = $routeParams.id; $scope.orgId = $routeParams.id;
$scope.org = {}; $scope.org = {};
$scope.events = {}; $scope.events = {};
@ -579,9 +602,11 @@ orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $ro
$scope.vocabularies = {}; $scope.vocabularies = {};
vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; }); vocabulariesService.getVocs(function(vocs) { $scope.vocabularies = vocs; });
if ($routeParams.msg == 1) { $scope.message = 'New organization registered !!!'; } if ($routeParams.msg == 1) { $scope.message = 'New organization registered !!!'; }
else if ($routeParams.msg == 2) { $scope.message = 'Organization updated !!!'; } else if ($routeParams.msg == 2) { $scope.message = 'Organization updated !!!'; }
else { $scope.message = ''; } else if ($routeParams.msg == 3) { $scope.message = 'Pending organization registered !!!'; }
else { $scope.message = ''; }
$window.scrollTo(0, 0);
$timeout(function() { $scope.message = ''; }, 3000) $timeout(function() { $scope.message = ''; }, 3000)
@ -589,8 +614,9 @@ orgsModule.controller('showEditCtrl', function ($scope, $http, $routeParams, $ro
orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routeParams, $location) { orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routeParams, $location) {
$scope.info = {}; $scope.info = {};
$scope.conflicts = []; $scope.pendingOrgs = [];
$scope.duplicates = []; $scope.duplicates = [];
$scope.conflicts = [];
$scope.currentTab = $routeParams.tab; $scope.currentTab = $routeParams.tab;
$scope.country = $routeParams.country; $scope.country = $routeParams.country;
@ -602,7 +628,7 @@ orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routePara
var found = ''; var found = '';
angular.forEach($scope.info.byCountry, function(values, c) { angular.forEach($scope.info.byCountry, function(values, c) {
if (!found && (($scope.currentTab == 1 && values.nDuplicates > 0) || ($scope.currentTab == 2 && values.nConflicts > 0))) { if (!found && (($scope.currentTab == 0 && values.nPendingOrgs > 0) || ($scope.currentTab == 1 && values.nDuplicates > 0) || ($scope.currentTab == 2 && values.nConflicts > 0))) {
found = c; found = c;
} }
}); });
@ -615,17 +641,22 @@ orgsModule.controller('showSuggestionsCtrl', function ($scope, $http, $routePara
$scope.refresh = function() { $scope.refresh = function() {
$scope.conflicts = []; $scope.pendingOrgs = [];
$scope.duplicates = []; $scope.duplicates = [];
$scope.conflicts = [];
if ($scope.country != '_') { if ($scope.country != '_') {
if ($scope.currentTab == 1) { if ($scope.currentTab == 0) {
$http.get('api/organizations/byCountry/pending/' + $scope.country).then(function successCallback(res) {
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
$scope.pendingOrgs = res.data;
}, function errorCallback(res) {
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
});
} else if ($scope.currentTab == 1) {
$http.get('api/organizations/duplicates/byCountry/' + $scope.country).then(function successCallback(res) { $http.get('api/organizations/duplicates/byCountry/' + $scope.country).then(function successCallback(res) {
if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); } if((typeof res.data) == 'string') { alert("Session expired !"); location.reload(true); }
$scope.duplicates = res.data; $scope.duplicates = res.data;
if ($scope.duplicates.length > 0) {
//$scope.currentCountry = $scope.duplicates.
}
}, function errorCallback(res) { }, function errorCallback(res) {
alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')'); alert('ERROR: ' + res.data.error + ' (' + res.data.message + ')');
}); });

View File

@ -45,7 +45,7 @@ fieldset > legend {
<div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent"> <div class="collapse navbar-collapse w-100 order-1" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link" href="#!/suggestions/_/1">Last suggestions</a></li> <li class="nav-item"><a class="nav-link" href="#!/suggestions/_/0">Last suggestions</a></li>
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a> <a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Search</a>
<div class="dropdown-menu"> <div class="dropdown-menu">