Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Skinia/Coordinators/AnalysisListCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ final class AnalysisListCoordinator: NavigationCoordinator, ObservableObject {

// MARK: - Navigation Methods
func showAnalysisDetail(for photo: SkinLesionPhoto) {
// This method is now handled directly in AnalysisListView
print("🔍 Navigation handled by AnalysisListView for photo: \(photo.id)")
// Navigation handled directly in AnalysisListView
}
}

Expand Down
2 changes: 0 additions & 2 deletions Skinia/Views/AnalysisList/AnalysisDetailSheetState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ final class AnalysisDetailSheetState: ObservableObject {
@Published var isShowing = false

func showSheet(with photo: SkinLesionPhoto) {
print("🔍 SheetState: Setting photo \(photo.id) and showing sheet")
selectedPhoto = photo
isShowing = true
}

func hideSheet() {
print("🔍 SheetState: Hiding sheet and clearing photo")
isShowing = false
selectedPhoto = nil
}
Expand Down
40 changes: 40 additions & 0 deletions Skinia/Views/AnalysisList/AnalysisDetailSheetView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import SwiftUI

struct AnalysisDetailSheetView: View {
@ObservedObject var sheetState: AnalysisDetailSheetState
let dependencyContainer: DependencyContainer

var body: some View {
Group {
if let photo = sheetState.selectedPhoto {
NavigationView {
AnalysisDetailView(
photo: photo,
photoRepository: dependencyContainer.photoRepository,
analysisService: dependencyContainer.analysisService
)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Fechar") {
sheetState.hideSheet()
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.environment(\.analysisService, dependencyContainer.analysisService)
.environment(\.notificationManager, dependencyContainer.notificationManager)
} else {
VStack(spacing: 12) {
Text("Error: Photo not found")
.foregroundColor(.red)
Button("Close") {
sheetState.hideSheet()
}
}
.padding()
}
}
}
}
40 changes: 40 additions & 0 deletions Skinia/Views/AnalysisList/AnalysisListSelectionActionsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import SwiftUI

struct AnalysisListSelectionActionsView: View {
@Binding var showingDeleteConfirmation: Bool
@Binding var activeAlert: AnalysisListAlertContext?
let onExportSelected: () -> AnalysisListAlertContext?
let onDeleteConfirmed: () -> Void

var body: some View {
Menu {
Button("Exportar Selecionadas") {
activeAlert = onExportSelected()
}

Button("Excluir Selecionadas", role: .destructive) {
HapticManager.shared.selection()
showingDeleteConfirmation = true
}
} label: {
Image(systemName: "ellipsis.circle")
}
.confirmationDialog(
"Confirmar exclusão",
isPresented: $showingDeleteConfirmation,
titleVisibility: .visible
) {
Button("Excluir", role: .destructive) {
HapticManager.shared.impact(.medium)
showingDeleteConfirmation = false
onDeleteConfirmed()
}

Button("Cancelar", role: .cancel) {
HapticManager.shared.selection()
}
} message: {
Text("Esta ação é irreversível. As análises selecionadas serão removidas permanentemente.")
}
}
}
40 changes: 40 additions & 0 deletions Skinia/Views/AnalysisList/AnalysisListStatisticsFormatter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation

enum AnalysisListStatisticsFormatter {
static func statisticsSummary(from photos: [SkinLesionPhoto]) -> String? {
guard !photos.isEmpty else { return nil }

let total = photos.count
let completed = photos.completedCount
let pending = photos.pendingCount
let failed = photos.failedCount
let highRisk = photos.highRiskCount

return """
Estatísticas de Análises
Total de fotos: \(total)
Concluídas: \(completed)
Em andamento: \(pending)
Com erro: \(failed)
Alto ou urgente risco: \(highRisk)
"""
}
}

private extension Array where Element == SkinLesionPhoto {
var completedCount: Int {
filter { $0.analysisStatus == .completed }.count
}

var pendingCount: Int {
filter { $0.isPendingAnalysis }.count
}

var failedCount: Int {
filter { $0.hasError }.count
}

var highRiskCount: Int {
filter { $0.analysisResult?.riskLevel == .high || $0.analysisResult?.riskLevel == .urgent }.count
}
}
63 changes: 63 additions & 0 deletions Skinia/Views/AnalysisList/AnalysisListToolbarContent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import SwiftUI

struct AnalysisListToolbarContent: ToolbarContent {
@ObservedObject var selectionHelper: AnalysisListSelectionHelper
@Binding var showingSearchField: Bool
@Binding var showingFilterSheet: Bool
@Binding var showingHistoryOptions: Bool
@Binding var showingDeleteConfirmation: Bool
@Binding var activeAlert: AnalysisListAlertContext?
let isFilterActive: Bool
let onExportSelected: () -> AnalysisListAlertContext?
let onExportAll: (AnalysisExportFormat) -> AnalysisListAlertContext?
let onDeleteSelected: () -> Void

var body: some ToolbarContent {
ToolbarItemGroup(placement: .navigationBarLeading) {
if selectionHelper.isSelectionMode {
Button("Cancelar") {
selectionHelper.clearSelection()
}
}
}

ToolbarItemGroup(placement: .navigationBarTrailing) {
if selectionHelper.isSelectionMode {
AnalysisListSelectionActionsView(
showingDeleteConfirmation: $showingDeleteConfirmation,
activeAlert: $activeAlert,
onExportSelected: onExportSelected,
onDeleteConfirmed: onDeleteSelected
)
} else {
Button {
showingSearchField.toggle()
} label: {
Image(systemName: "magnifyingglass")
}

Button {
showingFilterSheet = true
} label: {
Image(systemName: isFilterActive ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease.circle")
}

Menu {
Button("Estatísticas") {
showingHistoryOptions = true
}

Button("Exportar Todas") {
activeAlert = onExportAll(.pdf)
}

Button("Selecionar") {
selectionHelper.enableSelectionMode()
}
} label: {
Image(systemName: "ellipsis.circle")
}
}
}
}
}
Loading