Sep dept head form - #140
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for reassigning a department head during an employee separation workflow (UI capture/display plus backend/CLI deprovision handling), and extends “direct reports” fetching to optionally target a specific supervisor IAM ID.
Changes:
- Adds UI for selecting and displaying a “New Department Head” on separation create/single pages.
- Introduces
models.admin.separateDepartmentHead(separationId)and calls it during deprovision (API + CLI). - Updates the direct-reports API/client plumbing to accept an optional
iamIdquery parameter.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| services/lib/models/admin.js | Adds separateDepartmentHead and group head reassignment logic |
| services/lib/cork/services/EmployeeService.js | Adds iamId parameter support for direct reports requests |
| services/lib/cork/models/EmployeeModel.js | Propagates optional iamId argument + updates docs |
| services/cli/lib/employees.js | Calls separateDepartmentHead during CLI separation flow |
| services/app/client/src/elements/pages/ucdlib-iam-page-separation-single.tpl.js | Displays “New Department Head” on separation detail |
| services/app/client/src/elements/pages/ucdlib-iam-page-separation-single.js | Fetches/display data for the new department head |
| services/app/client/src/elements/pages/ucdlib-iam-page-separation-new.tpl.js | Conditionally shows employee search for new dept head |
| services/app/client/src/elements/pages/ucdlib-iam-page-separation-new.js | Captures/validates new dept head selection and persists to additionalData |
| services/app/api/separation.js | Calls separateDepartmentHead during deprovision |
| services/app/api/employees.js | Allows optional query override for direct reports IAM id |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
services/app/client/src/elements/pages/ucdlib-iam-page-separation-new.js:145
- When the selected employee is cleared (or changed),
isDepartmentHead/employeeNewHeadare not reset. This can leave the "New Department Head" field visible (and/or a staleemployeeNewHeadvalue) from a previous selection, which breaks the conditional UI and submit validation.
_onEmployeeStatusChange(e){
if ( e.detail.employee ){
this.employeeRecord = e.detail.employee;
this._isDepartmentHead();
} else {
this.employeeRecord = {};
}
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (3)
services/lib/cork/models/EmployeeModel.js:28
- The updated JSDoc for
getDirectReports()is inaccurate: the method does not accept a supervisor IAM id parameter and it returns the Cork store record (viaEmployeeService.getDirectReports()), not anArray<Object>. This can mislead callers and future maintenance.
/**
* @description Returns all direct reports for the current user (or for the provided supervisor IAM id)
* @returns {Array<Object>} direct report employee records
*/
getDirectReports(){
return this.service.getDirectReports();
}
services/app/api/separation.js:48
- Server-side validation for department-head separations currently only checks that
additionalData.newDepartmentHeadis present, but it does not verify that the selected employee exists in the local DB and is actually in the same department where the separating employee is head. This allows invalidnewDepartmentHeadvalues to be stored and later silently skipped during deprovisioning, which conflicts with the requirement that the new head must be selected and in the respective department.
//if the employee is a department head, add the department head info to the separation request
const isDepartmentHead = payload.additionalData.employeeRecord.groups.some(g => g.type === 'Department' && g.isHead === true);
if ( isDepartmentHead ) {
const newHeadId = payload.additionalData?.newDepartmentHead;
if ( !newHeadId ) {
res.status(400).json({error: true, message: 'Missing new department head id for department head separation or selected head was not in the same department as the separated employee.'});
return;
}
}
services/app/api/separation.js:292
- The deprovision endpoint logs
departmentSeparationResultto stdout. This can add noisy logs and may expose internal details/identifiers in production logs. Prefer structured logging behind an explicit debug flag, or remove this line.
// separate department head if applicable
const departmentSeparationResult = await models.admin.separateDepartmentHead(separationId);
console.log('departmentSeparationResult', departmentSeparationResult);
if ( departmentSeparationResult.error ) {
spelkey-ucd
left a comment
There was a problem hiding this comment.
In addition to the comments, please add a way for the submitter to bypass the department head checks.
Put a checkbox below the lookup tool with the label and description:
Do not assign new/interim head
If you do not assign a new head now, you will need to contact ITIS as soon as the information becomes available.
And then make a new CLI command:
separation replace-department-head <separation-id> <new-department-head>
which will set the appropriate data in the separation record
| * Returns blank array if no reports | ||
| */ | ||
| api.get('/employees/direct-reports', async (req, res) => { | ||
| const iamId = req.auth.token.iamId; |
There was a problem hiding this comment.
looks like all changes in this file slipped in from another issue. please revert.
There was a problem hiding this comment.
when you merged sandbox into your feature branch, you threw out several legitimate updates. please fix.
| if ( isDepartmentHead ) { | ||
| const newHeadId = payload.additionalData?.newDepartmentHead; | ||
| if ( !newHeadId ) { | ||
| res.status(400).json({error: true, message: 'Missing new department head id for department head separation or selected head was not in the same department as the separated employee.'}); |
There was a problem hiding this comment.
you dont actually validate that the new department head is in the department
| if ( !departmentRecord.res.rows.length ) { | ||
| return {error: true, message: `No department record found for id ${currentDepartmentId}`}; | ||
| } | ||
| const currentDepartment = departmentRecord.res.rows; |
There was a problem hiding this comment.
should be departmentRecord.res.rows[0]. Then you don't have to do this flatMap stuff
| * @description Checks if the employee is a department head | ||
| * @returns {boolean} | ||
| */ | ||
| async _isDepartmentHead(){ |
There was a problem hiding this comment.
isDepartmentHead depends solely on employeeRecord. A better pattern is to hook onto willUpdate. That way you always know it is never stale.
| additionalData.employeeId = this.employeeId; | ||
| additionalData.employeeUserId = this.userId; | ||
| this.employeeNewHead && this.employeeNewHead.iamId | ||
| ? additionalData.newDepartmentHead = this.employeeNewHead.iamId : delete additionalData.newDepartmentHead; |
There was a problem hiding this comment.
this is a weird pattern.
additionalData.newDepartmentHead = this.employeeNewHead?.iamId || null would be much simpler, yes?
| <input id='sp-separation-date' type="date" required .value=${this.separationDate} @input=${(e) => {this.separationDate = e.target.value;}}> | ||
| </div> | ||
| <div class="field-container" ?hidden=${!this.isDepartmentHead}> | ||
| <label for="sp-department-head">New Department Head <abbr title="Required">*</abbr></label> |
There was a problem hiding this comment.
- change label to "New or Interim Department Head"
- add a blurb that the user must be in the same department
| this.employeeNewHead = null; | ||
| return; | ||
| } | ||
| const r = await this.EmployeeModel.get(iamId, "iamId"); |
There was a problem hiding this comment.
this will fail if:
- the new department head leaves the library, so when you go to historical separation requests there is a chance you will get an error bar when nothing is actually wrong.
- If the user does not have admin or hr access (aka for the supervisor)
instead of doing an api call, set some additional properties on the separation object with the new department head's name in the POST api endpoint.
| utils.logObject(keycloakUser); | ||
| } | ||
|
|
||
| // separate department head if applicable |
There was a problem hiding this comment.
this is a local database action. so it should be under options.rm
#138