bcgov / bcgov/sdd-database

Holding non resident workspaces

Open
#178 0 comments 0 reactions 1 assignee Claimed by @darshanpandhi View on GitHub
Dominant language
TypeScript
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Prompt for ChatGPT

I want to implement a new feature in my Employee Information app called Assignment Holds. Please use the full context below to understand the business/domain reasoning, schema direction, UI direction, seed logic, and v1/v2 boundaries.

Current app context:
This is a Next.js/React app for Employee Information. It uses Prisma/Postgres. The main entities currently include:
- Employee
- Office
- Workspace
- Workstation
- WorkspaceAssignmentType
- WorkstationModel
- OhsAccommodationType
- related lookup tables

Current important schema concepts:
- Employee has office_number and workspace_assignment_type_id.
- Workspace represents a physical numbered workspace in a government office.
- Workspace has office_number, workspace_number, employee_id, restricted_program_area_id, notes, and is_on_hold.
- Workstation represents a physical device/asset.
- Office is a government office/location and currently has employees, workspaces, and workstations.
- WorkspaceAssignmentType currently has values like Resident, Mobile, Float, Offsite, Friendship Centre.

Important source data context:
The legacy source Access/Excel data has a column called something like “Workspace Number,” but that column is overloaded. It sometimes contains:
1. An actual physical workspace number, for example 123, 316, 1A, etc.
2. A workspace assignment type / space allocation type, for example Mobile, Float, Offsite, Friendship Centre.

The source data also has an “Assigned To” column. Sometimes Assigned To is a real employee/person. Sometimes Assigned To is HOLD. The Status/Notes column often contains position details when Assigned To = HOLD, for example text containing “Pos 00132580...” or similar.

Business clarification:
- Resident means the employee works in the office often enough to need a specific physical numbered workspace.
- Mobile means the employee belongs to that office and consumes some office capacity, but does not have a specific numbered workspace. They might work in office 2 days or less, including complete work-from-home.
- Float is similar to Mobile, but for auxiliary employees.
- Offsite means the employee is administratively tied to the office but does not consume government office space.
- Friendship Centre means the employee reports to or works in a partner office and does not consume government office space.
- Resident, Mobile, and Float can matter for office capacity/space planning.
- Offsite and Friendship Centre are still relevant for office reporting, but they do not consume direct government office space.

Important domain distinction:
Do not treat Mobile, Float, Offsite, or Friendship Centre as Workspace rows.
Workspace should mean physical numbered resident workspace only.
WorkspaceAssignmentType should describe how an employee or pending position is allocated: Resident, Mobile, Float, Offsite, Friendship Centre.
Workspace.is_on_hold should only mean that a specific physical numbered workspace is held/unavailable.

Current seed logic thinking:
When source row has Assigned To = HOLD and Workspace Number is an actual physical workspace number:
- This means a physical resident workspace is being held for a future incoming employee.
- Keep modeling this as Workspace.is_on_hold = true.
- Store the source Status/Notes text in Workspace.notes.
- Do not create an Employee for this row.

When source row has Assigned To = HOLD and Workspace Number is Mobile, Float, Offsite, or Friendship Centre:
- This is not a physical workspace hold.
- This means the team knows about a future/pending incoming hire or position, but does not yet have complete employee details like IDIR or employee_id.
- The source row is effectively a “future assignment / draft employee / capacity placeholder.”
- It should not be stored as Employee because there is no actual employee/person yet.
- It should not be stored as Workspace because there is no physical numbered workspace.
- This is the reason for the new AssignmentHold entity.

Desired concept:
AssignmentHold represents a pending/future position or draft assignment tied to an office and a non-resident workspace assignment type.
Internally we can call it AssignmentHold, even if UI wording is something like “Assignment Hold” or “Pending Assignment.”

Recommended v1 Prisma model:
Add:

model AssignmentHold {
id Int @id @default(autoincrement())
office_number String @db.VarChar(3)
workspace_assignment_type_id Int
notes String? @db.VarChar(2000)

office Office @relation(fields: [office_number], references: [office_number])
workspace_assignment_type WorkspaceAssignmentType @relation(fields: [workspace_assignment_type_id], references: [id])
}

Add relation to Office:
assignment_holds AssignmentHold[]

Add relation to WorkspaceAssignmentType:
assignment_holds AssignmentHold[]

Do NOT add position_number in v1.
Reason: source notes/status text may contain position number inconsistently, and keeping all text in notes is simpler. Position parsing can be v2.

Manual creation rule:
When users manually add an AssignmentHold, allow assignment type dropdown with:
- Mobile
- Float
- Offsite
- Friendship Centre

Do NOT allow Resident in AssignmentHold creation.
Reason: Resident holds should use physical Workspace.is_on_hold because a resident hold should be tied to a specific numbered workspace.

V1 user flow:
The end user hears about an incoming hire but does not have full employee details yet. They know office number, assignment type, and maybe notes/position info.
Flow:
1. User searches for an Office.
2. User opens the Office record/modal.
3. In Office modal, user sees an Assignment Holds accordion.
4. User clicks Add Assignment Hold.
5. User selects assignment type, excluding Resident.
6. User enters notes.
7. User saves.
8. Later, when complete employee information arrives, user can delete this AssignmentHold and manually create a real Employee.

V1 operations:
- Seed existing non-resident HOLD rows into AssignmentHold.
- Show AssignmentHolds in Office modal.
- Add AssignmentHold from Office modal.
- Delete AssignmentHold from Office modal.
- No edit for AssignmentHold in v1.
- No Convert to Employee in v1.
- No top-level AssignmentHold search tab in v1.

Future v2:
Add “Convert to Employee” button on AssignmentHold.
This would prefill EmployeeForm with:
- office_number
- workspace_assignment_type_id
- notes
Potentially delete AssignmentHold only after successful employee creation.
Do not implement this in v1 because it introduces complexity around cancel/save/deduplication.

Office modal direction:
Office modal should become the “office summary hub,” not just a view-only office record.

Recommended Office modal accordion structure:
1. Office Details
2. Office Summary
3. Assignment Holds

Potential future sections:
4. Employees
5. Workspaces
6. Workstations

For v1, the useful sections are:
- Office Details
- Office Summary
- Assignment Holds

Office Summary should eventually show read-only counts.

Employee assignment summary:
- Total employees in this office
- Resident employees
- Mobile employees
- Float employees
- Offsite employees
- Friendship Centre employees

Workspace status summary:
- Total workspaces in this office
- Occupied workspaces
- Available workspaces
- On Hold workspaces

Workspace status rules:
- Occupied = workspace.employee_id != null
- On Hold = workspace.is_on_hold === true
- Available = workspace.employee_id == null && workspace.is_on_hold === false

Assignment holds summary:
- Total assignment holds
- Mobile holds
- Float holds
- Offsite holds
- Friendship Centre holds

Important UI distinction:
Do not mix AssignmentHolds into employee counts. They are pending/future assignments, not actual employees.
If v2 needs projected counts, calculate projected counts separately as Employee count + AssignmentHold count.

Assignment Holds accordion:
Show actual hold records for that office.
For each hold, display:
- Assignment Type
- Notes
- Delete button

Also include:
- Add Assignment Hold button or inline form
- Assignment Type dropdown, excluding Resident
- Notes textarea
- Create/Cancel buttons

Data fetching / type direction:
Office likely needs to become a richer search result type, like Employee/Workspace/Workstation.
Currently OfficeEntity may be raw Office & { type: "office" }.
Change toward:
- Create officeSearchResultArgs in src/db/data-access/shared.ts
- Include:
- employees with workspace_assignment_type
- workspaces
- assignment_holds with workspace_assignment_type
- Then:
type OfficeSearchResult = Prisma.OfficeGetPayload
type OfficeEntity = OfficeSearchResult & { type: "office" }

For v1, counts can be calculated in frontend from included office relations. No need for DB aggregation yet unless performance requires it.

Possible officeSearchResultArgs shape:
export const officeSearchResultArgs = {
include: {
employees: {
include: {
workspace_assignment_type: true,
}
},
workspaces: true,
assignment_holds: {
include: {
workspace_assignment_type: true,
},
orderBy: {
id: "asc",
}
}
}
} satisfies Prisma.OfficeDefaultArgs

Implementation architecture:
Create db/data-access:
src/db/data-access/assignmentHolds.ts

Functions likely needed:
- addAssignmentHold(assignmentHold: AssignmentHoldFormValues)
- deleteAssignmentHold(id: number)
- maybe getAssignmentHoldsByOfficeNumber(officeNumber: string), only if not included via Office fetch

Create actions:
src/actions/entities/assignmentHold/actions.ts
src/actions/entities/assignmentHold/rules.ts
src/actions/entities/assignmentHold/errors.ts if needed

For v1, manual server actions are fine. No need to force createEntityActions unless it fits naturally.
Needed actions:
- addAssignmentHoldAction(prevState, formData)
- deleteAssignmentHoldAction(id)

Types:
Add AssignmentHoldFormValues:
{
office_number: string
workspace_assignment_type_id: number
notes: string | null
}

Parser:
Add parseAssignmentHoldFormData(formData), probably in utils.ts or a better parser location if we later split parsers.
It should parse:
- officeNumber hidden field or prop-originated office number
- workspaceAssignmentType select
- notes

Validation rules:
- office_number required and must exist.
- workspace_assignment_type_id required and must exist.
- assignment type must not be Resident.
- notes max length 2000.
- optional: no duplicate prevention in v1 unless business requests it.

Data access:
addAssignmentHold should create row.
deleteAssignmentHold should delete by id.

Delete behavior:
Delete button should probably ask confirmation if easy, but for v1 a simple delete action with success/error alert is acceptable if consistent with app.
After create/delete:
- refreshSearchResults()
- keep modal updated if possible.
Simplest might be refresh search results and close modal or rely on reopening.
Better v1 would update selected office state or refresh current viewed entity so Office modal updates immediately. If that is too much, close modal after create/delete and show success alert.

Seeding AssignmentHolds:
Add a seed step, probably seedAssignmentHolds.ts.
It should scan source rows where:
- Assigned To = HOLD
- Workspace Number is Mobile / Float / Offsite / Friendship Centre
Then create AssignmentHold:
- office_number from OfficeNum
- workspace_assignment_type_id from lookup by Workspace Number value
- notes from Status column or null
Do not create Employee for these rows.
Do not create Workspace for these rows.

Existing seed rules to preserve:
- Assigned To = HOLD + actual physical workspace number:
create Workspace row with is_on_hold = true and notes from Status.
- Assigned To = real employee + actual physical workspace number:
create Employee with workspace_assignment_type_id = Resident and workspace assignment handled by workspace seeding/sync.
- Assigned To = real employee + Mobile/Float/Offsite/Friendship Centre:
create Employee with workspace_assignment_type_id set accordingly and no Workspace row.

Potential seed duplicate handling:
AssignmentHolds may have duplicate office/type/notes. For v1, maybe allow duplicates because there may be multiple incoming hires for same office/type. Do not add unique constraint.

Position number:
Do not store separately in v1. Store full source Status as notes.
A future parser could extract position number with regex like /\b[Pp]os\s+(\d{6,10})\b/, but not now.

UI naming:
Internally call entity AssignmentHold.
UI label can be “Assignment Holds” or “Pending Assignment Holds.”
Avoid “Draft Employee” in the schema because it is not a real employee yet.

Domain boundary summary:
- Office = government office/location.
- Employee = real known employee/person.
- Workspace = physical numbered resident workspace only.
- Workstation = device/asset.
- WorkspaceAssignmentType = classification of work arrangement/capacity category.
- Workspace.is_on_hold = a specific physical workspace is held.
- AssignmentHold = pending/future non-resident assignment/capacity placeholder for an office.

Important caution:
Do not remodel Mobile/Float/Offsite/Friendship Centre as Workspace rows.
Do not put AssignmentHolds into Employee.
Do not mix AssignmentHolds into actual employee counts.
Do not implement space weights or direct capacity calculation in v1.

Potential future v2 fields:
WorkspaceAssignmentType could later get:
- consumes_office_space Boolean
- office_space_weight Decimal
For example:
Resident = consumes true, weight 1.00
Mobile = consumes true, weight maybe 0.50
Float = consumes true, weight maybe 0.50
Offsite = consumes false, weight 0.00
Friendship Centre = consumes false, weight 0.00
But do not add this now unless explicitly needed.

Potential future v2 features:
- Convert AssignmentHold to Employee.
- Edit AssignmentHold.
- AssignmentHold search/filter.
- Projected office counts.
- Space capacity calculation.
- Space-weight fields on WorkspaceAssignmentType.

Current implementation style:
Follow existing app patterns:
- Server actions under src/actions/entities/...
- DB access under src/db/data-access/...
- Prisma payload include args in src/db/data-access/shared.ts
- Hooks are organized by domain under src/hooks/entity, src/hooks/employee, src/hooks/workspace, src/hooks/workstation, etc.
- Use useActionState in form components.
- Success/error handlers refresh search results and show alerts.
- Keep v1 minimal but architecturally clean.

When helping implement, go step by step.
Start with schema/model and types, then seed/data-access/actions, then Office modal UI.
Do not jump into a huge all-at-once refactor.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.