added sorting to the UI and ViewModels

Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
Matt Bruce 2025-01-21 12:42:10 -06:00
parent da6e68c219
commit bc635714b4
3 changed files with 74 additions and 7 deletions

View File

@ -24,6 +24,7 @@ public class MockEmployeeService: EmployeeServiceProtocol {
return
}
wrapper = localData
sortEmployees()
}
public func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees {
@ -37,12 +38,9 @@ public class MockEmployeeService: EmployeeServiceProtocol {
//resort mock data
if sortField != self.sortField || sortOrder != self.sortOrder {
wrapper = .init(employees: wrapper.employees.sorted(by: sortField, with: sortOrder),
total: wrapper.employees.count,
page: page,
perPage: perPage)
self.sortField = sortField
self.sortOrder = sortOrder
sortEmployees()
}
let totalUsers = wrapper.employees.count
@ -57,6 +55,13 @@ public class MockEmployeeService: EmployeeServiceProtocol {
let pagedEmployees = Array(wrapper.employees[startIndex..<endIndex])
return .init(employees: pagedEmployees, total: totalUsers, page: page, perPage: perPage)
}
private func sortEmployees() {
wrapper = .init(employees: wrapper.employees.sorted(by: sortField, with: sortOrder),
total: wrapper.employees.count,
page: 1,
perPage: 10)
}
}
extension Array where Element == Employee {

View File

@ -68,6 +68,41 @@ class EmployeesViewController: UIViewController {
modeSegmentedControl.selectedSegmentIndex = 0
modeSegmentedControl.addTarget(self, action: #selector(onServiceModeChange), for: .valueChanged)
navigationItem.titleView = modeSegmentedControl
let sortButton = UIBarButtonItem(title: "Sort", style: .plain, target: self, action: #selector(showSortOptions))
navigationItem.rightBarButtonItem = sortButton
}
private func presentSortingActionSheet() {
let alert = UIAlertController(title: "Sort Employees", message: "Select a sort option", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Full Name (Asc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .fullName
self?.viewModel.sortOrder = .ascending
})
alert.addAction(UIAlertAction(title: "Full Name (Desc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .fullName
self?.viewModel.sortOrder = .descending
})
alert.addAction(UIAlertAction(title: "Team (Asc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .team
self?.viewModel.sortOrder = .ascending
})
alert.addAction(UIAlertAction(title: "Team (Desc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .team
self?.viewModel.sortOrder = .descending
})
alert.addAction(UIAlertAction(title: "Employee Type (Asc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .employeeType
self?.viewModel.sortOrder = .ascending
})
alert.addAction(UIAlertAction(title: "Employee Type (Desc)", style: .default) { [weak self] _ in
self?.viewModel.sortField = .employeeType
self?.viewModel.sortOrder = .ascending
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
/// Using the ViewModel setup combine handlers
@ -154,6 +189,11 @@ extension EmployeesViewController: UITableViewDelegate {
// Mark: - Objective-C Methods
extension EmployeesViewController {
/// Show sort options
@objc private func showSortOptions() {
presentSortingActionSheet()
}
/// Fetch the Employees
@objc private func didPullToRefresh() {
viewModel.fetchEmployees()

View File

@ -6,6 +6,7 @@
//
import Foundation
import Combine
/// ViewModel that will be bound to an Employees model and used
/// specifically with the EmployeesViewController.
@ -18,11 +19,27 @@ public class EmployeesViewModel: ObservableObject {
@Published public private(set) var isLoading: Bool = false
@Published public private(set) var hasMorePages: Bool = true
@Published public var sortField: EmployeeSortField = .fullName
@Published public var sortOrder: EmployeeSortOrder = .ascending
private var cancellables = Set<AnyCancellable>()
private var currentPage = 1
private let perPage = 10
private var totalEmployees = 0
public init() {}
public init() {
observeSortingChanges()
}
/// Observe changes to sortField and sortOrder and debounce fetch calls
private func observeSortingChanges() {
Publishers.CombineLatest($sortField, $sortOrder)
.debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
.sink { [weak self] _, _ in
self?.resetAndFetchEmployees()
}
.store(in: &cancellables)
}
/// Fetch employees for the given page
public func fetchEmployees(page: Int = 1) {
@ -35,8 +52,8 @@ public class EmployeesViewModel: ObservableObject {
// Fetch employees using the paginated API
let wrapper = try await employeeService.getEmployees(serviceMode, page: page,
perPage: perPage,
sortField: .employeeType,
sortOrder: .ascending)
sortField: sortField,
sortOrder: sortOrder)
// Update published properties
if page == 1 {
@ -67,6 +84,11 @@ public class EmployeesViewModel: ObservableObject {
/// Change the service mode (e.g., production, malformed, empty)
public func changeMode(to mode: EmployeeServiceMode) {
serviceMode = mode
resetAndFetchEmployees()
}
/// Resets the current employee list and fetches data from page 1
private func resetAndFetchEmployees() {
currentPage = 1
employees = []
hasMorePages = true