Skip to content

Sep dept head form - #140

Open
sbagg wants to merge 11 commits into
sandboxfrom
sepDeptHeadForm
Open

Sep dept head form#140
sbagg wants to merge 11 commits into
sandboxfrom
sepDeptHeadForm

Conversation

@sbagg

@sbagg sbagg commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@sbagg sbagg self-assigned this Jul 13, 2026
@sbagg
sbagg requested a review from Copilot July 13, 2026 07:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 iamId query 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.

Comment thread services/lib/models/admin.js
Comment thread services/lib/models/admin.js Outdated
Comment thread services/lib/models/admin.js Outdated
Comment thread services/lib/models/admin.js
Comment thread services/lib/models/admin.js Outdated
Comment thread services/app/api/separation.js
Comment thread services/lib/models/admin.js

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 / employeeNewHead are not reset. This can leave the "New Department Head" field visible (and/or a stale employeeNewHead value) 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 = {};
    }
  }

Comment thread services/lib/models/admin.js Outdated
Comment thread services/app/client/src/elements/pages/ucdlib-iam-page-separation-new.js Outdated
Comment thread services/app/api/separation.js
Comment thread services/app/api/separation.js
Comment thread services/lib/cork/models/EmployeeModel.js
Comment thread services/app/api/employees.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (via EmployeeService.getDirectReports()), not an Array<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.newDepartmentHead is 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 invalid newDepartmentHead values 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 departmentSeparationResult to 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 ) {

Comment thread services/lib/models/admin.js
Comment thread services/lib/models/admin.js Outdated
@sbagg
sbagg requested a review from spelkey-ucd July 22, 2026 19:47

@spelkey-ucd spelkey-ucd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like all changes in this file slipped in from another issue. please revert.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.'});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(){

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. change label to "New or Interim Department Head"
  2. add a blurb that the user must be in the same department

this.employeeNewHead = null;
return;
}
const r = await this.EmployeeModel.get(iamId, "iamId");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will fail if:

  1. 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.
  2. 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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a local database action. so it should be under options.rm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants