Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ddcf2b5
[autocommit] bumping build number
rob-ouser-bi Apr 21, 2025
554cad4
Merge branch 'develop' into bug/BI-2618
mlm483 Apr 30, 2025
1bdac90
Merge pull request #439 from Breeding-Insight/bug/BI-2618
mlm483 Apr 30, 2025
c594591
[autocommit] bumping build number
rob-ouser-bi Apr 30, 2025
4ad4781
[TEMPORARY] - enabled sub-entity feature
mlm483 May 6, 2025
8a3c290
[autocommit] bumping build number
rob-ouser-bi May 6, 2025
4038e23
[BI-2597] sort Exp Unit ID alphabitally AND numerically
davedrp May 1, 2025
fa2c717
Merge pull request #441 from Breeding-Insight/bug/BI-2597
davedrp May 6, 2025
1179a07
[autocommit] bumping build number
rob-ouser-bi May 6, 2025
8c3b8f1
[autocommit] bumping build number
rob-ouser-bi May 19, 2025
6f34305
Update version number
nickpalladino May 19, 2025
b52ad98
[autocommit] bumping build number
rob-ouser-bi May 19, 2025
fff1b4c
Try to get running for demo
nickpalladino Jun 3, 2025
0c082a6
[autocommit] bumping build number
rob-ouser-bi Jun 3, 2025
b5a7206
Updated duplicate message to be more accurate
nickpalladino Jun 17, 2025
a64d416
Merge pull request #444 from Breeding-Insight/bug/BI-2639
nickpalladino Jun 17, 2025
b3d79fb
[autocommit] bumping build number
rob-ouser-bi Jun 17, 2025
ea459c2
Update version
nickpalladino Jun 18, 2025
3c37b4d
[autocommit] bumping build number
rob-ouser-bi Jun 18, 2025
13d26ce
Fix version number
nickpalladino Jun 19, 2025
fc73038
[autocommit] bumping build number
rob-ouser-bi Jun 19, 2025
c826c51
Switch to using bi-api calls rather than doing check on front end
nickpalladino Jul 9, 2025
db352c3
Fix error message
nickpalladino Jul 10, 2025
d8489a3
Fixed async issues
nickpalladino Jul 10, 2025
599d7df
Update error messages
nickpalladino Jul 16, 2025
da6d685
Merge pull request #446 from Breeding-Insight/bug/BI-2691
nickpalladino Aug 1, 2025
90ea586
[autocommit] bumping build number
rob-ouser-bi Aug 1, 2025
5c4d461
Merge branch 'main' into release/1.1.1
mlm483 Aug 13, 2025
d7f9774
[autocommit] bumping build number
rob-ouser-bi Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bi-web",
"version": "v1.1.0",
"version": "v1.2.0+873",
"private": true,
"scripts": {
"build": "node $npm_package_config_task_path/build.js --dev-audit-level=critical --prod-audit-level=none",
Expand Down Expand Up @@ -96,5 +96,5 @@
"vue-cli-plugin-axios": "0.0.4",
"vue-template-compiler": "^2.7.14"
},
"versionInfo": "https://github.com/Breeding-Insight/bi-web/releases/tag/v1.1.0"
"versionInfo": "https://github.com/Breeding-Insight/bi-web/commit/5c4d461021cff1e8ce8e69a4b2de0d43468358d7"
}
35 changes: 35 additions & 0 deletions src/breeding-insight/dao/UserDAO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {BiResponse} from "@/breeding-insight/model/BiResponse";
import {Role} from "@/breeding-insight/model/Role";
import {PaginationQuery} from "@/breeding-insight/model/PaginationQuery";
import {UserSort} from "@/breeding-insight/model/Sort";
import {SearchRequest} from "@/breeding-insight/model/SearchRequest";

export class UserDAO {

Expand Down Expand Up @@ -101,6 +102,40 @@ export class UserDAO {

}

static getById(id: string): Promise<BiResponse> {
return new Promise<BiResponse>((resolve, reject) => {
api.call({ url: `${process.env.VUE_APP_BI_API_V1_PATH}/users/${id}`, method: 'get' })
.then((response: any) => {
const biResponse = new BiResponse(response.data);
resolve(biResponse);
}).catch((error) => reject(error));
});
}

static search(searchRequest: SearchRequest, {page, pageSize}: PaginationQuery, {field, order}: UserSort): Promise<BiResponse> {

return new Promise<BiResponse>(((resolve, reject) => {
const config = {
url: `${process.env.VUE_APP_BI_API_V1_PATH}/users/search`,
method: 'post',
data: searchRequest,
params: {
sortField: field,
sortOrder: order,
page,
pageSize
}
}
api.call(config)
.then((response: any) => {
const biResponse = new BiResponse(response.data);
resolve(biResponse);
}).catch((error) => {
reject(error);
})
}));
}

static updateSystemRoles(id: string, systemRoles: Array<Role>) {

return new Promise<BiResponse>((resolve, reject) => {
Expand Down
26 changes: 26 additions & 0 deletions src/breeding-insight/model/FilterRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export class FilterRequest {
field: string;
value: string;

constructor(field: string, value: string) {
this.field = field;
this.value = value;
}
}
26 changes: 26 additions & 0 deletions src/breeding-insight/model/SearchRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {FilterRequest} from "@/breeding-insight/model/FilterRequest";

export class SearchRequest {
filters: FilterRequest[] = [];

constructor(filters: FilterRequest[]) {
this.filters = filters;
}
}
44 changes: 43 additions & 1 deletion src/breeding-insight/service/UserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ import {Program} from "@/breeding-insight/model/Program";
import {ProgramUser} from "@/breeding-insight/model/ProgramUser";
import {PaginationQuery} from "@/breeding-insight/model/PaginationQuery";
import {ProgramSort, SortOrder, UserSort, UserSortField} from "@/breeding-insight/model/Sort";
import {SearchRequest} from "@/breeding-insight/model/SearchRequest";

export class UserService {

static duplicateEmailMessage : string = 'Email is already in use.';
static errorCreateUser: string = 'Unable to create user';
static errorDeleteUser: string = 'Unable to archive user';
static errorGetUsers: string = 'Error while trying to load roles';
static errorGetUsers: string = 'Error while trying to load users';
static errorSearchUsers: string = 'Error while searching users';
static errorGetUser: string = 'User not found';
static errorDeleteUserNotFound: string = 'Unable to find user to deactivate';
static errorDeleteUserNotAllowed: string = 'You are not allowed to deactivate this user.';
static errorPermissionsEditUser: string = "You don't have permissions to edit the roles of this user.";
Expand Down Expand Up @@ -153,6 +156,45 @@ export class UserService {
}));
}

static getById(id: string): Promise<User> {
return new Promise<User>((resolve, reject) => {
UserDAO.getById(id).then((biResponse: BiResponse) => {
const result: any = biResponse.result;
const role: Role | undefined = this.parseSystemRoles(result.systemRoles);
const programRoles: ProgramUser[] | undefined = this.parseProgramRoles(result.programRoles);
const user = new User(result.id, result.name, result.orcid, result.email, role, programRoles);
resolve(user);
}).catch((error) => {
error['errorMessage'] = this.errorGetUser;
reject(error);
});
});
}

static search(searchRequest: SearchRequest,
paginationQuery: PaginationQuery = new PaginationQuery(1, 50, true),
sort: UserSort = new UserSort(UserSortField.Name, SortOrder.Ascending)): Promise<[User[], Metadata]> {
return new Promise<[User[], Metadata]>(((resolve, reject) => {

UserDAO.search(searchRequest, paginationQuery, sort).then((biResponse) => {

// Parse our users into the vue users param
let users = biResponse.result.data.map((user: any) => {
const role: Role | undefined = this.parseSystemRoles(user.systemRoles);
const programRoles: ProgramUser[] | undefined = this.parseProgramRoles(user.programRoles);
return new User(user.id, user.name, user.orcid, user.email, role, programRoles);
});

resolve([users, biResponse.metadata]);

}).catch((error) => {
error['errorMessage'] = this.errorSearchUsers;
reject(error)
});

}));
}

static updateSystemRoles(user: User): Promise<User> {

return new Promise<User>((resolve, reject) => {
Expand Down
72 changes: 33 additions & 39 deletions src/components/program/ProgramUsersTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@
import {
UPDATE_PROGRAM_USER_SORT
} from "@/store/sorting/mutation-types";
import {SearchRequest} from "@/breeding-insight/model/SearchRequest";
import {FilterRequest} from "@/breeding-insight/model/FilterRequest";

@Component({
components: { ExpandableTable, NewDataForm, BasicInputField, BasicSelectField, TableColumn,
Expand Down Expand Up @@ -218,7 +220,6 @@ export default class ProgramUsersTable extends Vue {
private activeProgram?: Program;
private activeUser?: User;
public users: ProgramUser[] = [];
public systemUsers: User[] = [];

private deactivateActive: boolean = false;
private newUserActive: boolean = false;
Expand Down Expand Up @@ -252,7 +253,6 @@ export default class ProgramUsersTable extends Vue {

mounted() {
this.getRoles();
this.getSystemUsers();
this.paginationChanged();
}

Expand Down Expand Up @@ -333,41 +333,40 @@ export default class ProgramUsersTable extends Vue {

}

saveUser() {
async saveUser() {

this.newUser.program = this.activeProgram;

try {
this.newUser = this.checkExistingUserByEmail(this.newUser, this.systemUsers);
this.newUser = await this.checkExistingUserByEmail(this.newUser);
} catch (err) {
this.$emit('show-error-notification', err);
this.newUserFormState.bus.$emit(DataFormEventBusHandler.SAVE_COMPLETE_EVENT);
return;
}

// if we found a system user by email in checkExistingUserByEmail then system user exists
const systemUserExisted = this.newUser.id !== undefined;

ProgramUserService.create(this.newUser).then((user: ProgramUser) => {
this.paginationController.updatePage(1);
this.paginationController.updateOnAdd();
this.getUsers();
this.getSystemUsers();

// See if the user already existed
//TODO: Reconsider when user search feature is added
if (this.getSystemUserById(user, this.systemUsers)) {
if (systemUserExisted) {
this.$emit('show-success-notification', 'Success! Existing user ' + user.name + ' added to program.');
} else {
this.$emit('show-success-notification', 'Success! ' + this.newUser.name + ' added.');
}

if(this.newUser.email === this.activeUser!.email) this.updateActiveUser();

this.getSystemUsers();
this.newUser = new ProgramUser();
this.newUserActive = false;
}).catch((error) => {
this.$emit('show-error-notification', error.errorMessage);
this.getUsers();
this.getSystemUsers();
}).finally(() => this.newUserFormState.bus.$emit(DataFormEventBusHandler.SAVE_COMPLETE_EVENT))

}
Expand All @@ -379,47 +378,42 @@ export default class ProgramUsersTable extends Vue {
Vue.prototype.$ability.update(rules);
}

//TODO: Reconsider when user search feature is added
getSystemUsers() {
//TODO: Do we still want this since orcid entry is removed?
async checkExistingUserByEmail(user: ProgramUser): Promise<ProgramUser> {
user.id = undefined;
// api call to check if user exists for email
// this was done on front-end before because we didn't have search endpoint at the time
const filter = new FilterRequest('email', user.email);
const request = new SearchRequest(filter);

UserService.getAll().then(([users, metadata]) => {
this.systemUsers = users;
}).catch((error) => {
try {
const [users, metadata] = await UserService.search(request);
// email exists
if (users.length === 1) {
user.id = users[0].id;
}
} catch (error) {
// Display error that users cannot be loaded
this.$emit('show-error-notification', 'Error while trying to load system users');
throw error;
});

}

//TODO: Reconsider when user search feature is added
//TODO: Do we still want this since orcid entry is removed?
checkExistingUserByEmail(user: ProgramUser, systemUsers: User[]): ProgramUser {
user.id = undefined;
let usersFound = 0;
for (const systemUser of systemUsers){
if (user.email === systemUser.email){
usersFound += 1;
if (systemUser.id){
user.id = systemUser.id;
}
}
}

if (usersFound > 1){
throw "Email matches two different users.";
}

return user;
}

//TODO: Reconsider when user search feature is added
getSystemUserById(user: ProgramUser, systemUsers: User[]): User | undefined {
for (const systemUser of systemUsers){
if (user.id === systemUser.id){
return systemUser;
getSystemUserById(user: ProgramUser): User | undefined {

UserService.getById(user.id).then(([users, metadata]) => {
if (users.length === 1) {
return users[0];
}
}
}).catch((error) => {
// Display error that users cannot be loaded
this.$emit('show-error-notification', 'Error while trying to load system user');
throw error;
});

return undefined;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export default class ExperimentDetails extends ProgramsBase {
new ActionMenuItem('experiment-import-file', 'import-file', 'Import file', this.$ability.can('create', 'Import')),
new ActionMenuItem('experiment-download-file', 'download-file', 'Download file'),
new ActionMenuItem('experiment-add-collaborator', 'add-collaborator', 'Add Collaborator', this.$ability.can('manage', 'Collaborator')),
// new ActionMenuItem('experiment-create-sub-entity-dataset', 'create-sub-entity-dataset', 'Create Sub-Entity Dataset'),
new ActionMenuItem('experiment-create-sub-entity-dataset', 'create-sub-entity-dataset', 'Create Sub-Entity Dataset'),
new ActionMenuItem('experiment-delete', 'delete-experiment', 'Delete experiment', this.$ability.can('delete', 'Experiment'))
];

Expand Down
13 changes: 13 additions & 0 deletions src/views/import/Dataset.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
<b-table-column
v-slot="props"
field="data.expUnitId"
:custom-sort="sortExpUnitId"
label="Exp Unit ID"
sortable
searchable
Expand Down Expand Up @@ -403,6 +404,18 @@ export default class Dataset extends ProgramsBase {
}
}

// This sorts the Exp Unit ID's in Alphanumeric order (ie. B300,BO2, B1 would sort to B1, B02, B300)
sortExpUnitId(a: any, b: any, isAsc: boolean){
let first :any = (a.data.expUnitId);
let second :any = (b.data.expUnitId);
if (isAsc) {
return first.toString().localeCompare(second.toString(), 'en', {numeric: true});
}
else {
return second.toString().localeCompare(first.toString(), 'en', {numeric: true});
}
}

/*
* remove the '[....]' found at the end of the string
* */
Expand Down
3 changes: 1 addition & 2 deletions src/views/import/ImportGermplasm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@
<p>New Germplasm count: {{ statistics.Germplasm.newObjectCount }}</p>
<p>New Pedigree Connections: {{ statistics["Pedigree Connections"].newObjectCount }}</p>
<template v-if="duplicatesPresent(rows)">
<p>Duplicate names detected and are highlighted in yellow and show a <alert-triangle-icon size="1.2x" class="icon-align"/> icon.
Upon import duplicates will become independent database entries.</p>
<p>Duplicate names detected and are highlighted in yellow and show a <alert-triangle-icon size="1.2x" class="icon-align"/> icon.</p>
</template>

</div>
Expand Down
3 changes: 3 additions & 0 deletions task/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ const port = process.env.PORT || JSON.parse(fs.readFileSync('package.json', 'utf
(async () => {
let spinner = ora({prefixText: ' ', color: 'yellow'});
try {
// TODO: figure out the issue here
/*
spinner = spinner.start('sort package.json');
await execa('npx', ['sort-package-json@3.0.0'], {preferLocal: true});
spinner = spinner.clear()
.succeed('package.json sorted');
*/

console.log(`App running at http://localhost:${port}`);
await execa.command(`vue-cli-service\ serve --port ${port}`);
Expand Down
Loading