-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added cypress tests for shifting request #9035
base: develop
Are you sure you want to change the base?
added cypress tests for shifting request #9035
Conversation
WalkthroughThe changes introduce a new end-to-end test file, Changes
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
✅ Deploy Preview for care-ohc ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Outside diff range and nitpick comments (12)
cypress/pageobject/Shift/ShiftDetails.ts (2)
2-4
: Add verification and error handlingThe click action should be followed by verification and include error handling for better test reliability.
clickUpdateStatusButton(): Cypress.Chainable<void> { - cy.contains("Update Status/Details").click() + return cy.get('[data-testid="shift-update-status-button"]') + .should('be.visible') + .should('be.enabled') + .click() + .should('not.be.disabled'); }
1-6
: Fix code formatting issuesThere are inconsistent spacing issues in the class definition.
export class ShiftDetails { clickUpdateStatusButton(): Cypress.Chainable<void> { cy.get('[data-testid="shift-update-status-button"]') .should('be.visible') .click(); } - }
cypress/pageobject/Shift/ShiftingBoard.ts (1)
3-3
: Consider using data-testid attributes for more robust selectors.The current selectors (
#patient_name
and text content "All Details") could be fragile if the UI changes. Using data-testid attributes would make the tests more maintainable.Consider updating the selectors:
- cy.get('#patient_name').type(patientName); + cy.get('[data-testid="patient-name-input"]').type(patientName); - cy.contains("All Details").click(); + cy.get('[data-testid="view-details-button"]').click();Also applies to: 6-6
cypress/pageobject/Shift/ShiftUpdate.ts (1)
1-1
: Consider removing the default export.Having both named and default exports is redundant. Stick with the named export for better tree-shaking and consistency with TypeScript best practices.
export class ShiftUpdate { // ... class implementation } -export default ShiftUpdate;
Also applies to: 26-26
cypress/pageobject/Shift/ShiftCreation.ts (2)
18-20
: Consider adding phone number validation.While the implementation is correct, consider adding input validation to ensure the phone number format is valid before typing.
typeAmbulancePhone(number: string) { + const phoneRegex = /^\d{10}$/; // Adjust regex based on your phone number format + if (!phoneRegex.test(number)) { + throw new Error('Invalid phone number format'); + } cy.get("#ambulance_phone_number").click().type(number); }
26-28
: Fix code style inconsistencies.The method has several style inconsistencies compared to other methods in the class:
- Single quotes instead of double quotes
- Missing semicolon
- Inconsistent spacing in parameter declaration
- typeComment(comment:string) { - cy.get('#comments').click().type(comment) + typeComment(comment: string) { + cy.get("#comments").click().type(comment); }cypress/e2e/shifting_spec/ShiftingRequest.cy.ts (4)
1-16
: Consider using consistent variable declaration style.While the page object imports follow good POM practices, the variable declarations could be more consistent.
Apply this diff to maintain consistency:
const loginPage = new LoginPage(); - const shiftCreation = new ShiftCreation() - const shiftingBoard = new ShiftingBoard() - const shiftDetails = new ShiftDetails() - const shiftUpdate = new ShiftUpdate() - const patientPage = new PatientPage() - const patientConsultationPage = new PatientConsultationPage() + const shiftCreation = new ShiftCreation(); + const shiftingBoard = new ShiftingBoard(); + const shiftDetails = new ShiftDetails(); + const shiftUpdate = new ShiftUpdate(); + const patientPage = new PatientPage(); + const patientConsultationPage = new PatientConsultationPage();
27-47
: Enhance test robustness with data validation and custom commands.While the test covers the basic flow well, consider these improvements:
- Add validation for phone number format
- Extract common operations into custom commands
- Add assertions for form field values before submission
Example implementation:
// In cypress/support/commands.ts Cypress.Commands.add('validatePhoneNumber', (number: string) => { const phoneRegex = /^[6-9]\d{9}$/; // Indian mobile number format expect(number).to.match(phoneRegex); }); // In the test shiftCreation.typeCurrentFacilityPhone("9465666768"); cy.validatePhoneNumber("9465666768"); // Add form field validations cy.get('[data-testid=facility-person]').should('have.value', 'new'); cy.get('[data-testid=shift-reason]').should('have.value', 'emmergency');
77-80
: Consider adding proper test data cleanup.While localStorage cleanup is handled, consider adding cleanup for test data created during the tests.
afterEach(() => { cy.saveLocalStorage(); // Clean up test data cy.request({ method: 'DELETE', url: '/api/v1/shift', qs: { patient_name: 'Dummy Patient 16' } }).then((response) => { expect(response.status).to.eq(200); }); });
1-80
: Consider expanding test coverage for comprehensive validation.The tests follow the POM approach and cover basic functionality well. However, consider adding these scenarios:
- Validation of shifting board UI elements
- Filtering and sorting on shifting board
- Edge cases for patient selection
- Cancellation of shifting requests
Would you like assistance in implementing these additional test scenarios?
cypress/pageobject/Patient/PatientConsultation.ts (2)
116-124
: Implementation looks good, consider improving wait handling.The implementation follows the Page Object Model pattern and maintains consistency with existing methods. However, consider replacing the hardcoded
wait(3000)
with Cypress's built-in retry-ability and assertions:clickShiftPatientButton() { cy.get("#consultation-buttons").scrollIntoView(); cy.get("button").contains("Manage Patient").click(); cy.verifyAndClickElement( "#consultation-buttons", "Shift Patient", ); - cy.wait(3000); + // Wait for any specific element or state that indicates the action is complete + cy.get('body').should('not.have.class', 'loading'); }
115-124
: Consider refactoring to reduce code duplication.The "Manage Patient" button click logic is duplicated between
clickShiftPatientButton()
andclickEditConsultationButton()
. Consider extracting this common functionality:+ private clickManagePatientButton() { + cy.get("#consultation-buttons").scrollIntoView(); + cy.get("button").contains("Manage Patient").click(); + } clickEditConsultationButton() { - cy.get("#consultation-buttons").scrollIntoView(); - cy.get("button").contains("Manage Patient").click(); + this.clickManagePatientButton(); cy.verifyAndClickElement( "#consultation-buttons", "Edit Consultation Details", ); cy.wait(3000); } clickShiftPatientButton() { - cy.get("#consultation-buttons").scrollIntoView(); - cy.get("button").contains("Manage Patient").click(); + this.clickManagePatientButton(); cy.verifyAndClickElement( "#consultation-buttons", "Shift Patient", ); cy.wait(3000); }Also applies to: 114-114
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (6)
cypress/e2e/shifting_spec/ShiftingRequest.cy.ts
(1 hunks)cypress/pageobject/Patient/PatientConsultation.ts
(2 hunks)cypress/pageobject/Shift/ShiftCreation.ts
(1 hunks)cypress/pageobject/Shift/ShiftDetails.ts
(1 hunks)cypress/pageobject/Shift/ShiftUpdate.ts
(1 hunks)cypress/pageobject/Shift/ShiftingBoard.ts
(1 hunks)
🔇 Additional comments (6)
cypress/pageobject/Shift/ShiftDetails.ts (1)
1-6
: Verify integration with shifting request workflow
The class seems minimal for handling shift details. Consider if additional methods are needed based on the PR objectives.
Consider adding methods for other common shift operations mentioned in the PR objectives, such as:
- Verifying shift status
- Handling form submissions
- Validating shift details
✅ Verification successful
Let me gather more information about the shift-related test files to better understand the workflow and verify if additional methods are needed.
ShiftDetails class is appropriately scoped for its role in the workflow
After analyzing the shifting request workflow through the test files and related page objects, the ShiftDetails
class is correctly implemented for its specific responsibility. The class serves as a transition point between viewing shift details and initiating updates, which is evident from the test scenario in ShiftingRequest.cy.ts
. The update functionality is properly delegated to the ShiftUpdate
class, while creation is handled by ShiftCreation
class, following a clear separation of concerns.
The workflow shows:
ShiftDetails
is used solely to transition from viewing to editing mode- Actual form interactions are handled by dedicated
ShiftUpdate
andShiftCreation
classes - The minimal implementation aligns with its single responsibility in the workflow
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for other shift-related test files to understand the complete workflow
echo "Searching for related shift test files..."
fd -e ts -e js "Shift.*\.(ts|js)" cypress/
echo "\nChecking for shift-related data-testid attributes in the codebase..."
rg -l "data-testid.*shift"
echo "\nChecking for other shift-related Cypress commands..."
rg "cy\.(get|contains).*shift" cypress/
Length of output: 1240
Script:
#!/bin/bash
# Check contents of shift-related test files for workflow understanding
echo "Checking ShiftingRequest.cy.ts for workflow steps..."
cat cypress/e2e/shifting_spec/ShiftingRequest.cy.ts
echo "\nChecking ShiftUpdate.ts for update-related methods..."
cat cypress/pageobject/Shift/ShiftUpdate.ts
echo "\nChecking ShiftCreation.ts for creation workflow..."
cat cypress/pageobject/Shift/ShiftCreation.ts
Length of output: 4946
cypress/pageobject/Shift/ShiftingBoard.ts (1)
1-1
: LGTM! Well-structured POM class.
The class follows the Page Object Model pattern and has a clear, descriptive name that reflects its responsibility.
cypress/pageobject/Shift/ShiftUpdate.ts (1)
1-26
: Verify test coverage for shift update scenarios.
Let's ensure that all methods in this POM class are utilized in the test cases.
✅ Verification successful
Let me try a different approach to find test coverage since the previous command had issues with the file type recognition. This should help us locate both the ShiftUpdate usage and relevant test files in the Cypress directory.
I see that ShiftUpdate is imported and instantiated in ShiftingRequest.cy.ts. Let's examine how its methods are actually used in the test file to verify the coverage.
Test coverage is complete for the ShiftUpdate page object
All methods in the ShiftUpdate class are being utilized in the test case:
typeShiftReason()
typeAmbulanceDriverName()
typeAmbulancePhone()
typeAmbulanceNumber()
typeComment()
submitShiftForm()
The test also includes proper assertions to verify the submitted data through API interception.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for usage of ShiftUpdate methods in test files
echo "Searching for test coverage of ShiftUpdate methods..."
rg -t typescript "ShiftUpdate" cypress/e2e/
Length of output: 195
Script:
#!/bin/bash
# Search for test files and ShiftUpdate usage with ripgrep
echo "Searching for test files and ShiftUpdate usage..."
rg "ShiftUpdate" cypress/
# Also check for spec/test files specifically
echo -e "\nLooking for spec/test files..."
fd -e spec.ts -e spec.tsx -e test.ts -e test.tsx -e cy.ts . cypress/
Length of output: 2109
Script:
#!/bin/bash
# Let's examine the actual usage of ShiftUpdate methods in ShiftingRequest.cy.ts
echo "Checking ShiftUpdate usage in ShiftingRequest.cy.ts..."
rg -A 10 "shiftUpdate" cypress/e2e/shifting_spec/ShiftingRequest.cy.ts
Length of output: 1519
cypress/pageobject/Shift/ShiftCreation.ts (3)
14-16
: LGTM! Method follows POM pattern and Cypress best practices.
The implementation is consistent with the class's existing methods and uses stable ID selectors.
22-24
: LGTM! Consistent implementation following best practices.
The method maintains consistency with the class's pattern and uses appropriate selectors.
14-28
: Verify complete coverage of shift creation fields.
Let's ensure all required fields for shift creation are covered by the page object methods.
✅ Verification successful
All required shift creation fields are properly covered
The page object methods correctly match all the mandatory fields in the shift creation form:
- Required fields from the form component:
- Referring facility contact name and number
- Reason for shifting
- Patient category
- Optional fields also covered:
- Ambulance driver details (name, phone, number)
- Comments
The implementation aligns with both the form component (ShiftCreate.tsx
) and its usage in tests (ShiftingRequest.cy.ts
).
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for form field IDs in the codebase to verify coverage
echo "Searching for form field IDs in shift creation components..."
rg -t tsx -t jsx "id=['\"].*['\"]" --glob 'src/**/shift*'
# Search for cy.get() calls in existing shift-related tests
echo "Checking existing shift-related tests for field interactions..."
rg "cy\.get\(" --glob 'cypress/**/*shift*.{ts,js,tsx,jsx}'
Length of output: 569
Script:
#!/bin/bash
# Search for form field IDs in the codebase with correct file extensions
echo "Searching for form field IDs in shift creation components..."
rg -t ts -t js -t tsx -t jsx "id=['\"].*['\"]"
# Search for cy.get() calls in shift-related tests
echo "Checking shift-related tests for field interactions..."
rg "cy\.get\(" cypress/pageobject/Shift/ShiftCreation.ts
# Search for other potential shift creation files
echo "Looking for shift creation related files..."
fd -e ts -e js -e tsx -e jsx shift -i
Length of output: 1804
Script:
#!/bin/bash
# Check the ShiftCreate component for form fields
echo "Checking ShiftCreate component for form fields..."
cat src/components/Patient/ShiftCreate.tsx
# Check the shifting request test for additional field validations
echo "Checking shifting request test..."
cat cypress/e2e/shifting_spec/ShiftingRequest.cy.ts
Length of output: 15592
export class ShiftDetails{ | ||
clickUpdateStatusButton(){ | ||
cy.contains("Update Status/Details").click() | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add TypeScript types and documentation
The class should follow TypeScript best practices with proper type annotations and documentation.
+/**
+ * Page Object Model class for handling shift details operations
+ */
export class ShiftDetails{
- clickUpdateStatusButton(){
+ /**
+ * Clicks the update status button in the shift details view
+ * @returns {Cypress.Chainable<void>} Cypress chain
+ */
+ clickUpdateStatusButton(): Cypress.Chainable<void> {
cy.contains("Update Status/Details").click()
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export class ShiftDetails{ | |
clickUpdateStatusButton(){ | |
cy.contains("Update Status/Details").click() | |
} | |
} | |
/** | |
* Page Object Model class for handling shift details operations | |
*/ | |
export class ShiftDetails{ | |
/** | |
* Clicks the update status button in the shift details view | |
* @returns {Cypress.Chainable<void>} Cypress chain | |
*/ | |
clickUpdateStatusButton(): Cypress.Chainable<void> { | |
cy.contains("Update Status/Details").click() | |
} | |
} |
@@ -0,0 +1,6 @@ | |||
export class ShiftDetails{ | |||
clickUpdateStatusButton(){ | |||
cy.contains("Update Status/Details").click() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use data-testid for more robust element selection
Using text content for element selection is fragile and can break if the text changes or with i18n. Use data-testid attributes instead.
- cy.contains("Update Status/Details").click()
+ cy.get('[data-testid="shift-update-status-button"]').click()
Committable suggestion skipped: line range outside the PR's diff.
cy.intercept("GET", "**/api/v1/shift/**").as("getShiftingRequest"); | ||
cy.wait("@getShiftingRequest").its("response.statusCode").should("eq", 200); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Make the API intercept pattern more specific to match the actual endpoint
Based on the search results, the broad pattern **/api/v1/shift/**
could intercept multiple endpoints:
/api/v1/shift/
(GET, POST)/api/v1/shift/{id}/
(GET, PUT, DELETE)/api/v1/shift/{id}/comment/
(GET, POST)/api/v1/shift/{externalId}/transfer/
(POST)
Since this intercept is used in the openDetails
method and followed by a status check, it should specifically target the list endpoint:
cy.intercept("GET", "/api/v1/shift/").as("getShiftingRequest");
🔗 Analysis chain
Verify API endpoint pattern and response handling.
The API interception pattern **/api/v1/shift/**
is quite broad. Consider making it more specific to avoid intercepting unintended requests.
Let's verify the API endpoint pattern:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for shift-related API endpoints to ensure we're using the correct pattern
rg -g '*.{ts,js,tsx,jsx}' --no-heading "api/v1/shift" -A 2 -B 2
Length of output: 3699
openDetails(patientName) { | ||
cy.get('#patient_name').type(patientName); | ||
cy.intercept("GET", "**/api/v1/shift/**").as("getShiftingRequest"); | ||
cy.wait("@getShiftingRequest").its("response.statusCode").should("eq", 200); | ||
cy.contains("All Details").click(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add TypeScript type safety and improve error handling.
The method implementation could be more robust with the following improvements:
- Add type safety for the parameter
- Configure appropriate timeout for API wait
- Add error handling for failed scenarios
Consider applying these improvements:
- openDetails(patientName) {
+ openDetails(patientName: string) {
cy.get('#patient_name').type(patientName);
cy.intercept("GET", "**/api/v1/shift/**").as("getShiftingRequest");
- cy.wait("@getShiftingRequest").its("response.statusCode").should("eq", 200);
+ cy.wait("@getShiftingRequest", { timeout: 10000 })
+ .then((interception) => {
+ if (interception.response?.statusCode !== 200) {
+ throw new Error(`API request failed with status ${interception.response?.statusCode}`);
+ }
+ });
- cy.contains("All Details").click();
+ cy.contains("All Details", { timeout: 5000 }).click();
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
openDetails(patientName) { | |
cy.get('#patient_name').type(patientName); | |
cy.intercept("GET", "**/api/v1/shift/**").as("getShiftingRequest"); | |
cy.wait("@getShiftingRequest").its("response.statusCode").should("eq", 200); | |
cy.contains("All Details").click(); | |
} | |
openDetails(patientName: string) { | |
cy.get('#patient_name').type(patientName); | |
cy.intercept("GET", "**/api/v1/shift/**").as("getShiftingRequest"); | |
cy.wait("@getShiftingRequest", { timeout: 10000 }) | |
.then((interception) => { | |
if (interception.response?.statusCode !== 200) { | |
throw new Error(`API request failed with status ${interception.response?.statusCode}`); | |
} | |
}); | |
cy.contains("All Details", { timeout: 5000 }).click(); | |
} |
typeShiftReason(reason: string) { | ||
cy.get("#reason").click().clear().type(reason); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance form interaction methods with better practices.
The input methods could be improved with:
- Input validation
- Wait assertions
- Better error handling
- JSDoc documentation
Here's a suggested implementation pattern:
/** Constants file */
export const SELECTORS = {
REASON: '#reason',
DRIVER_NAME: '#ambulance_driver_name',
// ... other selectors
} as const;
/** Example improved method */
/**
* Types the shift reason into the reason field
* @param reason - The reason for the shift
* @throws {Error} If the reason field is not found
*/
typeShiftReason(reason: string): void {
if (!reason?.trim()) {
throw new Error('Shift reason cannot be empty');
}
cy.get(SELECTORS.REASON)
.should('be.visible')
.click()
.clear()
.type(reason, { delay: 50 })
.should('have.value', reason);
}
Also applies to: 6-8, 10-12, 14-16, 18-20
submitShiftForm() { | ||
cy.get("#submit").contains("Submit").click(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve form submission handling.
The submit method should be more robust by:
- Using a more specific selector
- Handling loading states
- Verifying successful submission
/**
* Submits the shift form and verifies successful submission
* @throws {Error} If submission fails or times out
*/
submitShiftForm(): void {
cy.get('#submit')
.should('be.visible')
.and('not.be.disabled')
.click();
// Wait for loading state
cy.get('.loading-indicator').should('exist');
// Verify successful submission
cy.get('.success-message')
.should('be.visible')
.and('contain', 'Shift updated successfully');
}
it("Editing and verifying refelctions in existing request", () => { | ||
cy.intercept("PUT", "**/api/v1/shift/**").as("shiftUpdateRequest"); | ||
cy.visit("/shifting/board"); | ||
cy.contains("Dummy Patient 16").should("exist"); | ||
|
||
shiftingBoard.openDetails("Dummy Patient 16") | ||
shiftDetails.clickUpdateStatusButton() | ||
|
||
shiftUpdate.typeShiftReason("new reason"); | ||
shiftUpdate.typeAmbulanceDriverName("Ramesh"); | ||
shiftUpdate.typeAmbulancePhone("9755443232") | ||
shiftUpdate.typeAmbulanceNumber("2") | ||
shiftUpdate.typeComment("New comment") | ||
shiftUpdate.submitShiftForm(); | ||
|
||
cy.wait('@shiftUpdateRequest').then((interception) => { | ||
const responseData = interception.response.body; | ||
|
||
expect(responseData.patient_object.name).to.eq("Dummy Patient 16"); | ||
expect(responseData.ambulance_phone_number).to.eq("+919755443232"); | ||
expect(responseData.comments).to.eq("New comment"); | ||
expect(responseData.reason).to.eq("new reason"); | ||
expect(responseData.ambulance_driver_name).to.eq("Ramesh"); | ||
expect(responseData.ambulance_number).to.eq("2"); | ||
}); | ||
|
||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix typo and enhance error handling scenarios.
- The test description contains a typo: "refelctions" should be "reflections"
- Consider adding test cases for error scenarios
Add error scenarios:
it("Should handle API errors when updating shift request", () => {
cy.intercept("PUT", "**/api/v1/shift/**", {
statusCode: 500,
body: { error: "Internal Server Error" }
}).as("failedUpdate");
// Perform update operation
// Verify error handling in UI
cy.get('[data-testid=error-message]').should('be.visible');
});
it("Should validate required fields when updating", () => {
shiftingBoard.openDetails("Dummy Patient 16");
shiftDetails.clickUpdateStatusButton();
// Clear required fields
shiftUpdate.typeShiftReason("");
shiftUpdate.submitShiftForm();
// Verify validation messages
cy.get('[data-testid=reason-error]').should('be.visible');
});
Proposed Changes
@ohcnetwork/care-fe-code-reviewers
Merge Checklist
Summary by CodeRabbit
Release Notes
These updates improve the overall user experience in managing patient shifts efficiently.