Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2025-03-15 14:07:32 -05:00
parent cb69829d26
commit 0e138544a7

View File

@ -9,8 +9,13 @@ import SwiftUI
struct ContentView: View { struct ContentView: View {
let service: EmployeeDirectoryService let service: EmployeeDirectoryService
@Environment(\.editMode) private var editMode
var body: some View { var body: some View {
EmployeeDirectoryList(viewModel: .init(service: service)) EmployeeDirectoryList(viewModel: .init(service: service))
.onChange(of: editMode?.wrappedValue) { oldValue, newValue in
print("Edit mode changed from \(String(describing: oldValue)) to \(String(describing: newValue))")
}
} }
} }
@ -22,6 +27,8 @@ struct ContentView: View {
public struct EmployeeDirectoryList: View { public struct EmployeeDirectoryList: View {
@State public var viewModel: EmployeesViewModel @State public var viewModel: EmployeesViewModel
@State public var path = NavigationPath() @State public var path = NavigationPath()
@State private var selectedEmployees = Set<Employee.ID>()
@State private var editMode: EditMode = .inactive
init(viewModel: EmployeesViewModel? = nil) { init(viewModel: EmployeesViewModel? = nil) {
_viewModel = .init(wrappedValue: viewModel ?? .init(service: EmployeeService())) _viewModel = .init(wrappedValue: viewModel ?? .init(service: EmployeeService()))
@ -29,26 +36,23 @@ public struct EmployeeDirectoryList: View {
public var body: some View { public var body: some View {
NavigationStack(path: $path) { NavigationStack(path: $path) {
List { List(selection: $selectedEmployees) {
ForEach(viewModel.employees, id: \.id) { employee in ForEach(viewModel.employees, id: \.id) { employee in
let employeeViewModel = EmployeeViewModel(employee: employee) let employeeViewModel = EmployeeViewModel(employee: employee)
HStack (spacing: 10) { HStack(spacing: 10) {
ProfileImageView(urlString: employeeViewModel.smallPhoto, size: 51) ProfileImageView(urlString: employeeViewModel.smallPhoto, size: 51)
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text(employeeViewModel.fullName) Text(employeeViewModel.fullName)
.font(.headline) .font(.headline)
if let bio = employeeViewModel.biography { if let bio = employeeViewModel.biography {
Text(bio) Text(bio)
.font(.footnote) .font(.footnote)
.foregroundColor(.gray) .foregroundColor(.gray)
.lineLimit(2) .lineLimit(2)
} }
Label("Email: \(employeeViewModel.emailAddress)", systemImage: "envelope.fill") Label("Email: \(employeeViewModel.emailAddress)", systemImage: "envelope.fill")
.foregroundColor(.gray) .foregroundColor(.gray)
.font(.caption) .font(.caption)
if let phoneNumber = employeeViewModel.phoneNumber { if let phoneNumber = employeeViewModel.phoneNumber {
Label("Call: \(phoneNumber)", systemImage: "phone.fill") Label("Call: \(phoneNumber)", systemImage: "phone.fill")
.foregroundColor(.gray) .foregroundColor(.gray)
@ -57,15 +61,15 @@ public struct EmployeeDirectoryList: View {
} }
} }
.onTapGesture { .onTapGesture {
path.append(employee) if editMode != .active {
path.append(employee)
}
} }
.swipeActions(edge: .leading) { .swipeActions(edge: .leading) {
Button("Pin") { Button("Pin") {
print("\(employee.firstName) pinned") print("\(employee.firstName) pinned")
} }
.tint(.orange) .tint(.orange)
}
.swipeActions(edge: .leading) {
Button("Flagged") { Button("Flagged") {
print("\(employee.firstName) flagged") print("\(employee.firstName) flagged")
} }
@ -74,7 +78,7 @@ public struct EmployeeDirectoryList: View {
} }
.onMove(perform: moveEmployee) .onMove(perform: moveEmployee)
.onDelete(perform: deleteEmployees) .onDelete(perform: deleteEmployees)
if viewModel.hasNextPage { if viewModel.hasNextPage {
ProgressView() ProgressView()
.frame(maxWidth: .infinity, alignment: .center) .frame(maxWidth: .infinity, alignment: .center)
@ -84,7 +88,9 @@ public struct EmployeeDirectoryList: View {
} }
} }
.refreshable { .refreshable {
await viewModel.refresh() if editMode != .active {
await viewModel.refresh()
}
} }
.navigationTitle("Employee Directory") .navigationTitle("Employee Directory")
.navigationDestination(for: Employee.self) { employee in .navigationDestination(for: Employee.self) { employee in
@ -98,7 +104,24 @@ public struct EmployeeDirectoryList: View {
} }
.toolbar { .toolbar {
EditButton() EditButton()
.onTapGesture {
editMode = editMode == .active ? .inactive : .active
}
} }
.toolbar {
if !selectedEmployees.isEmpty {
ToolbarItemGroup(placement: .bottomBar) {
Button("Delete") {
withAnimation {
viewModel.delete(selectedEmployees: selectedEmployees)
selectedEmployees.removeAll()
}
}
Spacer()
}
}
}
.environment(\.editMode, $editMode)
} }
} }
@ -226,6 +249,10 @@ public class EmployeesViewModel {
public func delete(employee: Employee) { public func delete(employee: Employee) {
employees.removeAll { $0.id == employee.id } employees.removeAll { $0.id == employee.id }
} }
public func delete(selectedEmployees: Set<Employee.ID>) {
employees.removeAll { selectedEmployees.contains($0.id) }
}
} }
public struct EmployeeViewModel { public struct EmployeeViewModel {