You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
PadelClub/PadelClub/Views/Cashier/CashierView.swift

316 lines
10 KiB

//
// CashierView.swift
// Padel Tournament
//
// Created by Razmig Sarkissian on 04/03/2023.
//
import SwiftUI
import Combine
struct CashierView: View {
@EnvironmentObject var dataStore: DataStore
var tournaments : [Tournament]
var teams: [TeamRegistration]
@State private var sortOption: SortOption = .callDate
@State private var filterOption: FilterOption = .all
@State private var sortOrder: SortOrder = .ascending
@State private var searchText = ""
@State private var isSearching: Bool = false
init(tournament: Tournament, teams: [TeamRegistration]) {
self.tournaments = [tournament]
self.teams = teams
if tournament.hasEnded(), tournament.players().anySatisfy({ $0.hasPaid() == false }) {
_filterOption = .init(wrappedValue: .didNotPay)
}
if teams.filter({ $0.callDate != nil }).isEmpty {
_sortOption = .init(wrappedValue: .teamRank)
} else {
_sortOption = .init(wrappedValue: .callDate)
}
}
private func _sharedData() -> String {
let players = teams.filter({ _shouldDisplayTeam($0) })
.flatMap({ $0.players().filter({ _shouldDisplayPlayer($0) }) })
.map {
[$0.pasteData()]
.compacted()
.joined(separator: "\n")
}
.joined(separator: "\n\n")
return players
}
enum SortOption: Int, Identifiable, CaseIterable {
case teamRank
case alphabeticalLastName
case alphabeticalFirstName
case playerRank
case age
case callDate
var id: Int { self.rawValue }
func localizedLabel() -> String {
switch self {
case .callDate:
return "Convocation"
case .teamRank:
return "Poids d'équipe"
case .alphabeticalLastName:
return "Nom"
case .alphabeticalFirstName:
return "Prénom"
case .playerRank:
return "Rang"
case .age:
return "Âge"
}
}
}
enum FilterOption: Int, Identifiable, CaseIterable {
case all
case didPay
case didNotPay
var id: Int { self.rawValue }
func localizedLabel() -> String {
switch self {
case .all:
return "Tous"
case .didPay:
return "Réglé"
case .didNotPay:
return "Non réglé"
}
}
func shouldDisplayPlayer(_ player: PlayerRegistration) -> Bool {
switch self {
case .all:
return true
case .didPay:
return player.hasPaid()
case .didNotPay:
return player.hasPaid() == false
}
}
}
var body: some View {
List {
if isSearching == false {
Section {
Picker(selection: $filterOption) {
ForEach(FilterOption.allCases) { filterOption in
Text(filterOption.localizedLabel()).tag(filterOption)
}
} label: {
Text("Statut du règlement")
}
Picker(selection: $sortOption) {
ForEach(SortOption.allCases) { sortOption in
Text(sortOption.localizedLabel()).tag(sortOption)
}
} label: {
Text("Affichage par")
}
Picker(selection: $sortOrder) {
Text("Croissant").tag(SortOrder.ascending)
Text("Décroissant").tag(SortOrder.descending)
} label: {
Text("Trier par ordre")
}
} header: {
Text("Options d'affichage")
}
}
if _isContentUnavailable() {
_contentUnavailableView()
}
switch sortOption {
case .teamRank:
_byTeamRankView()
case .alphabeticalLastName:
_byPlayerLastName()
case .alphabeticalFirstName:
_byPlayerFirstName()
case .playerRank:
_byPlayerRank()
case .age:
_byPlayerAge()
case .callDate:
_byCallDateView()
}
}
.headerProminence(.increased)
.searchable(text: $searchText, isPresented: $isSearching, prompt: Text("Chercher un joueur"))
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
ShareLink(item: _sharedData().createTxtFile("bilan"))
}
}
}
@ViewBuilder
func computedPlayerView(_ player: PlayerRegistration) -> some View {
EditablePlayerView(player: player, editingOptions: [.licenceId, .name, .payment])
}
private func _shouldDisplayTeam(_ team: TeamRegistration) -> Bool {
team.players().anySatisfy({
_shouldDisplayPlayer($0)
})
}
private func _shouldDisplayPlayer(_ player: PlayerRegistration) -> Bool {
if searchText.isEmpty == false {
filterOption.shouldDisplayPlayer(player) && player.contains(searchText)
} else {
filterOption.shouldDisplayPlayer(player)
}
}
@ViewBuilder
private func _byPlayer(_ players: [PlayerRegistration]) -> some View {
let _players = sortOrder == .ascending ? players : players.reversed()
ForEach(_players) { player in
Section {
computedPlayerView(player)
} header: {
HStack {
if let teamCallDate = player.team()?.callDate {
Text(teamCallDate.localizedDate())
}
Spacer()
Text(player.computedRank.formatted())
}
} footer: {
if tournaments.count > 1, let tournamentTitle = player.tournament()?.tournamentTitle() {
Text(tournamentTitle)
}
}
}
}
@ViewBuilder
private func _byPlayerRank() -> some View {
let players = teams.flatMap({ $0.players() }).sorted(using: .keyPath(\.computedRank)).filter({ _shouldDisplayPlayer($0) })
_byPlayer(players)
}
@ViewBuilder
private func _byPlayerAge() -> some View {
let players = teams.flatMap({ $0.players() }).filter({ $0.computedAge != nil }).sorted(using: .keyPath(\.computedAge!)).filter({ _shouldDisplayPlayer($0) })
_byPlayer(players)
}
@ViewBuilder
private func _byPlayerLastName() -> some View {
let players = teams.flatMap({ $0.players() }).sorted(using: .keyPath(\.lastName)).filter({ _shouldDisplayPlayer($0) })
_byPlayer(players)
}
@ViewBuilder
private func _byPlayerFirstName() -> some View {
let players = teams.flatMap({ $0.players() }).sorted(using: .keyPath(\.firstName)).filter({ _shouldDisplayPlayer($0) })
_byPlayer(players)
}
@ViewBuilder
private func _byTeamRankView() -> some View {
let _teams = sortOrder == .ascending ? teams : teams.reversed()
ForEach(_teams) { team in
if _shouldDisplayTeam(team) {
Section {
_cashierPlayersView(team.players())
} header: {
HStack {
if let callDate = team.callDate {
Text(callDate.localizedDate())
}
Spacer()
Text(team.weight.formatted())
}
} footer: {
if tournaments.count > 1, let tournamentTitle = team.tournamentObject()?.tournamentTitle() {
Text(tournamentTitle)
}
}
}
}
}
@ViewBuilder
private func _byCallDateView() -> some View {
let groupedTeams = Dictionary(grouping: teams) { team in
team.callDate
}
let keys = sortOrder == .ascending ? groupedTeams.keys.compactMap { $0 }.sorted() : groupedTeams.keys.compactMap { $0 }.sorted().reversed()
ForEach(keys, id: \.self) { key in
if let _teams = groupedTeams[key] {
ForEach(_teams) { team in
if _shouldDisplayTeam(team) {
Section {
_cashierPlayersView(team.players())
} header: {
Text(key.localizedDate())
} footer: {
if tournaments.count > 1, let tournamentTitle = team.tournamentObject()?.tournamentTitle() {
Text(tournamentTitle)
}
}
}
}
}
}
}
@ViewBuilder
private func _cashierPlayersView(_ players: [PlayerRegistration]) -> some View {
ForEach(players) { player in
if _shouldDisplayPlayer(player) {
computedPlayerView(player)
}
}
}
private func _isContentUnavailable() -> Bool {
switch sortOption {
case .callDate:
return teams.filter({ $0.callDate != nil && _shouldDisplayTeam($0) }).isEmpty
case .teamRank:
return teams.filter({ _shouldDisplayTeam($0) }).isEmpty
default:
return teams.flatMap({ $0.players() }).filter({ _shouldDisplayPlayer($0) }).isEmpty
}
}
private func _unavailableIcon() -> String {
switch sortOption {
case .teamRank, .callDate:
return "person.2.slash.fill"
default:
return "person.slash.fill"
}
}
@ViewBuilder
private func _contentUnavailableView() -> some View {
if isSearching {
ContentUnavailableView.search(text: searchText)
} else {
ContentUnavailableView("Aucun résultat", systemImage: _unavailableIcon())
}
}
}