multistore
Razmig Sarkissian 2 years ago
parent f06a54cc67
commit 18c744b34a
  1. 3
      PadelClub/ViewModel/SearchViewModel.swift
  2. 10
      PadelClub/Views/Navigation/MainView.swift
  3. 61
      PadelClub/Views/Player/Components/PlayerPopoverView.swift
  4. 32
      PadelClub/Views/Shared/SelectablePlayerListView.swift
  5. 52
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift

@ -27,7 +27,8 @@ class SearchViewModel: ObservableObject, Identifiable {
@Published var sortOption: SortOption = .rank @Published var sortOption: SortOption = .rank
@Published var selectedPlayers: Set<ImportedPlayer> = Set() @Published var selectedPlayers: Set<ImportedPlayer> = Set()
@Published var filterSelectionEnabled: Bool = false @Published var filterSelectionEnabled: Bool = false
var forcedSearch = false @Published var isPresented: Bool = false
var mostRecentDate: Date? = nil var mostRecentDate: Date? = nil
var selectionIsOver: Bool { var selectionIsOver: Bool {

@ -48,11 +48,11 @@ struct MainView: View {
await self._checkSourceFileAvailability() await self._checkSourceFileAvailability()
await self._downloadPreviousDate() await self._downloadPreviousDate()
} }
.refreshable { // .refreshable {
Task { // Task {
await self._checkSourceFileAvailability() // await self._checkSourceFileAvailability()
} // }
} // }
.overlay(alignment: .bottom) { .overlay(alignment: .bottom) {
if importingFiles { if importingFiles {
_activityStatusBoxView() _activityStatusBoxView()

@ -29,10 +29,10 @@ struct PlayerPopoverView: View {
@FocusState private var licenseIsFocused: Bool @FocusState private var licenseIsFocused: Bool
@FocusState private var amountIsFocused: Bool @FocusState private var amountIsFocused: Bool
static var source: String? @State private var source: String?
init(sex: Int, requiredField: [PlayerCreationField] = [.firstName, .lastName], creationCompletionHandler: @escaping (PlayerRegistration) -> Void) { init(source: String?, sex: Int, requiredField: [PlayerCreationField] = [.firstName, .lastName], creationCompletionHandler: @escaping (PlayerRegistration) -> Void) {
let source = PlayerPopoverView.source let source = source
if let source { if let source {
let words = source.components(separatedBy: .whitespaces) let words = source.components(separatedBy: .whitespaces)
if words.isEmpty == false { if words.isEmpty == false {
@ -51,29 +51,28 @@ struct PlayerPopoverView: View {
self.requiredField = requiredField self.requiredField = requiredField
self.creationCompletionHandler = creationCompletionHandler self.creationCompletionHandler = creationCompletionHandler
self.pasteBoard = UIPasteboard.general.string _source = State(wrappedValue: source)
} }
@State private var pasteBoard: String?
var body: some View { var body: some View {
NavigationStack { NavigationStack {
List { List {
if let pasteBoard { if let source {
Section { Section {
Text(pasteBoard).foregroundColor(.clear).padding(8) Text(source).foregroundColor(.clear).padding(8)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.overlay( .overlay(
TextEditor(text: .constant(pasteBoard)) TextEditor(text: .constant(source))
) )
.frame(minHeight: 20.0) .frame(minHeight: 20.0)
} header: { } header: {
HStack { HStack {
Spacer() Spacer()
Button { Button(role: .cancel) {
self.pasteBoard = "" self.source = nil
} label: { } label: {
Text("effacer le contenu du presse-papier") Text("effacer le contenu du presse-papier")
.font(.caption)
} }
.buttonStyle(.borderless) .buttonStyle(.borderless)
} }
@ -95,6 +94,8 @@ struct PlayerPopoverView: View {
Spacer() Spacer()
TextField("Prénom", text: $firstName) TextField("Prénom", text: $firstName)
.submitLabel(.next) .submitLabel(.next)
.keyboardType(.alphabet)
.textInputAutocapitalization(.words)
.focused($firstNameIsFocused) .focused($firstNameIsFocused)
.onSubmit { .onSubmit {
firstName = firstName.trimmed firstName = firstName.trimmed
@ -106,8 +107,10 @@ struct PlayerPopoverView: View {
Text("Nom").foregroundStyle(.secondary) Text("Nom").foregroundStyle(.secondary)
Spacer() Spacer()
TextField("Nom", text: $lastName) TextField("Nom", text: $lastName)
.focused($lastNameIsFocused)
.submitLabel(.next) .submitLabel(.next)
.textInputAutocapitalization(.words)
.keyboardType(.alphabet)
.focused($lastNameIsFocused)
.onSubmit { .onSubmit {
lastName = lastName.trimmed lastName = lastName.trimmed
licenseIsFocused = true licenseIsFocused = true
@ -119,7 +122,8 @@ struct PlayerPopoverView: View {
Spacer() Spacer()
TextField("Licence", text: $license) TextField("Licence", text: $license)
.focused($licenseIsFocused) .focused($licenseIsFocused)
.keyboardType(.namePhonePad) .textInputAutocapitalization(.never)
.keyboardType(.numberPad)
.submitLabel(.next) .submitLabel(.next)
.onSubmit { .onSubmit {
license = license.trimmed license = license.trimmed
@ -138,21 +142,23 @@ struct PlayerPopoverView: View {
HStack { HStack {
Text("Rang").foregroundStyle(.secondary) Text("Rang").foregroundStyle(.secondary)
Spacer() Spacer()
TextField("Non classé", value: $rank, format: .number) TextField("Non Classé" + (sex == 1 ? "" : "e"), value: $rank, format: .number)
.focused($amountIsFocused) .focused($amountIsFocused)
.keyboardType(.asciiCapable) .textInputAutocapitalization(.never)
.keyboardType(.numberPad)
.submitLabel(.done) .submitLabel(.done)
.fixedSize() .fixedSize()
} }
} header: { } header: {
HStack { HStack {
Spacer() Spacer()
Button { Button(role: .cancel) {
let last = firstName let last = firstName
firstName = lastName firstName = lastName
lastName = last lastName = last
} label: { } label: {
Text("inverser nom & prénom") Text("inverser nom & prénom")
.font(.caption)
}.buttonStyle(.borderless) }.buttonStyle(.borderless)
} }
.textCase(nil) .textCase(nil)
@ -192,6 +198,27 @@ struct PlayerPopoverView: View {
dismiss() dismiss()
} }
} }
if amountIsFocused || licenseIsFocused {
ToolbarItem(placement: .keyboard) {
Button("Confirmer") {
if licenseIsFocused {
license = license.trimmed
if requiredField.contains(.license) {
if license.isLicenseNumber {
amountIsFocused = true
} else {
displayWrongLicenceError = true
}
} else {
amountIsFocused = true
}
} else {
amountIsFocused = false
}
}
}
}
} }
.alert("Attention", isPresented: $displayWrongLicenceError) { .alert("Attention", isPresented: $displayWrongLicenceError) {
Button("OK") { Button("OK") {
@ -223,7 +250,7 @@ struct PlayerPopoverView: View {
} }
#Preview { #Preview {
PlayerPopoverView(sex: 1) { player in PlayerPopoverView(source: "Razmig Sarkissian", sex: 1) { player in
} }
} }

@ -34,6 +34,7 @@ struct SelectablePlayerListView: View {
self.playerSelectionAction = playerSelectionAction self.playerSelectionAction = playerSelectionAction
self.contentUnavailableAction = contentUnavailableAction self.contentUnavailableAction = contentUnavailableAction
let searchViewModel = SearchViewModel() let searchViewModel = SearchViewModel()
searchViewModel.isPresented = allowSelection != 0
searchViewModel.user = user searchViewModel.user = user
searchViewModel.allowSelection = allowSelection searchViewModel.allowSelection = allowSelection
searchViewModel.codeClub = fromPlayer?.clubCode ?? codeClub searchViewModel.codeClub = fromPlayer?.clubCode ?? codeClub
@ -65,9 +66,11 @@ struct SelectablePlayerListView: View {
} }
MySearchView(searchViewModel: searchViewModel, contentUnavailableAction: contentUnavailableAction) MySearchView(searchViewModel: searchViewModel, contentUnavailableAction: contentUnavailableAction)
.environment(\.editMode, searchViewModel.allowMultipleSelection ? .constant(.active) : .constant(.inactive)) .environment(\.editMode, searchViewModel.allowMultipleSelection ? .constant(.active) : .constant(.inactive))
.searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, isPresented: $searchViewModel.isPresented, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in
Text(token.shortLocalizedLabel) Text(token.shortLocalizedLabel)
}) })
.keyboardType(.alphabet)
.autocorrectionDisabled()
// .searchSuggestions({ // .searchSuggestions({
// ForEach(searchViewModel.suggestedTokens) { token in // ForEach(searchViewModel.suggestedTokens) { token in
// Button { // Button {
@ -261,24 +264,6 @@ struct MySearchView: View {
.listStyle(.grouped) .listStyle(.grouped)
.headerProminence(.increased) .headerProminence(.increased)
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.onChange(of: searchViewModel.searchText) {
search()
}
.onChange(of: searchViewModel.filterOption) {
search()
}
.onChange(of: searchViewModel.dataSet) {
search()
}
.onChange(of: searchViewModel.sortOption) {
sort()
}
.onChange(of: searchViewModel.ascending) {
sort()
}
.onChange(of: searchViewModel.hideAssimilation) {
search()
}
} }
var specificBugFixUUID: String { var specificBugFixUUID: String {
@ -311,6 +296,7 @@ struct MySearchView: View {
} header: { } header: {
Text(searchViewModel.selectedPlayers.count.formatted() + " " + searchViewModel.filterOption.localizedPlayerLabel + searchViewModel.selectedPlayers.count.pluralSuffix) Text(searchViewModel.selectedPlayers.count.formatted() + " " + searchViewModel.filterOption.localizedPlayerLabel + searchViewModel.selectedPlayers.count.pluralSuffix)
} }
} else if (searchViewModel.isPresented == true && searchViewModel.dataSet == .national && searchViewModel.searchText.isEmpty == true) {
} else { } else {
Section { Section {
ForEach(players, id: \.self) { player in ForEach(players, id: \.self) { player in
@ -455,12 +441,4 @@ struct MySearchView: View {
} }
} }
} }
private func search() {
//players.nsPredicate = searchViewModel.predicate()
}
private func sort() {
//players.nsSortDescriptors = searchViewModel.nsSortDescriptors()
}
} }

@ -23,12 +23,13 @@ struct InscriptionManagerView: View {
@State private var presentPlayerCreation: Bool = false @State private var presentPlayerCreation: Bool = false
@State private var presentImportView: Bool = false @State private var presentImportView: Bool = false
@State private var createdPlayers: Set<PlayerRegistration> = Set() @State private var createdPlayers: Set<PlayerRegistration> = Set()
@State private var testCreatedPlayers: Set<String> = Set() @State private var createdPlayerIds: Set<String> = Set()
@State private var editedTeam: TeamRegistration? @State private var editedTeam: TeamRegistration?
@State private var pasteString: String? @State private var pasteString: String?
@State private var currentRankSourceDate: Date? @State private var currentRankSourceDate: Date?
@State private var confirmUpdateRank = false @State private var confirmUpdateRank = false
@State private var updatingRank = false @State private var updatingRank = false
@State private var selectionSearchField: String?
let slideToDeleteTip = SlideToDeleteTip() let slideToDeleteTip = SlideToDeleteTip()
let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip() let inscriptionManagerWomanRankTip = InscriptionManagerWomanRankTip()
@ -54,6 +55,10 @@ struct InscriptionManagerView: View {
} }
} }
private func _searchSource() -> String? {
selectionSearchField ?? pasteString
}
private func _pastePredicate(pasteField: String, mostRecentDate: Date?) -> NSPredicate? { private func _pastePredicate(pasteField: String, mostRecentDate: Date?) -> NSPredicate? {
let text = pasteField.canonicalVersion let text = pasteField.canonicalVersion
@ -95,7 +100,7 @@ struct InscriptionManagerView: View {
private func _currentSelection() -> Set<PlayerRegistration> { private func _currentSelection() -> Set<PlayerRegistration> {
var currentSelection = Set<PlayerRegistration>() var currentSelection = Set<PlayerRegistration>()
testCreatedPlayers.compactMap { id in createdPlayerIds.compactMap { id in
fetchPlayers.first(where: { id == $0.license }) fetchPlayers.first(where: { id == $0.license })
}.forEach { player in }.forEach { player in
let player = PlayerRegistration(importedPlayer: player) let player = PlayerRegistration(importedPlayer: player)
@ -103,7 +108,7 @@ struct InscriptionManagerView: View {
currentSelection.insert(player) currentSelection.insert(player)
} }
testCreatedPlayers.compactMap { id in createdPlayerIds.compactMap { id in
createdPlayers.first(where: { id == $0.id }) createdPlayers.first(where: { id == $0.id })
}.forEach { }.forEach {
currentSelection.insert($0) currentSelection.insert($0)
@ -114,22 +119,22 @@ struct InscriptionManagerView: View {
private func _createTeam() { private func _createTeam() {
tournament.addTeam(_currentSelection()) tournament.addTeam(_currentSelection())
createdPlayers.removeAll() createdPlayers.removeAll()
testCreatedPlayers.removeAll() createdPlayerIds.removeAll()
pasteString = nil pasteString = nil
} }
private func _updateTeam() { private func _updateTeam() {
editedTeam?.updatePlayers(_currentSelection()) editedTeam?.updatePlayers(_currentSelection())
createdPlayers.removeAll() createdPlayers.removeAll()
testCreatedPlayers.removeAll() createdPlayerIds.removeAll()
pasteString = nil pasteString = nil
editedTeam = nil editedTeam = nil
} }
private func _buildingTeamView() -> some View { private func _buildingTeamView() -> some View {
List(selection: $testCreatedPlayers) { List(selection: $createdPlayerIds) {
Section { Section {
ForEach(testCreatedPlayers.sorted(), id: \.self) { id in ForEach(createdPlayerIds.sorted(), id: \.self) { id in
if let p = createdPlayers.first(where: { $0.id == id }) { if let p = createdPlayers.first(where: { $0.id == id }) {
PlayerView(player: p).tag(p.id) PlayerView(player: p).tag(p.id)
} }
@ -143,7 +148,7 @@ struct InscriptionManagerView: View {
} }
if editedTeam == nil { if editedTeam == nil {
if testCreatedPlayers.isEmpty { if createdPlayerIds.isEmpty {
RowButtonView(title: "Bloquer une place") { RowButtonView(title: "Bloquer une place") {
_createTeam() _createTeam()
} }
@ -169,7 +174,7 @@ struct InscriptionManagerView: View {
Button("effacer", role: .destructive) { Button("effacer", role: .destructive) {
self.pasteString = nil self.pasteString = nil
self.createdPlayers.removeAll() self.createdPlayers.removeAll()
self.testCreatedPlayers.removeAll() self.createdPlayerIds.removeAll()
} }
.buttonStyle(.borderless) .buttonStyle(.borderless)
} }
@ -204,7 +209,7 @@ struct InscriptionManagerView: View {
.onReceive(fetchPlayers.publisher.count()) { _ in // <-- here .onReceive(fetchPlayers.publisher.count()) { _ in // <-- here
if let pasteString, count == 2 { if let pasteString, count == 2 {
fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in fetchPlayers.filter { $0.hitForSearch(pasteString) >= hitTarget }.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) }).forEach { player in
testCreatedPlayers.insert(player.license!) createdPlayerIds.insert(player.license!)
} }
} }
} }
@ -307,7 +312,7 @@ struct InscriptionManagerView: View {
editedTeam = team editedTeam = team
team.unsortedPlayers().forEach { player in team.unsortedPlayers().forEach { player in
createdPlayers.insert(player) createdPlayers.insert(player)
testCreatedPlayers.insert(player.id) createdPlayerIds.insert(player.id)
} }
} }
Divider() Divider()
@ -325,12 +330,14 @@ struct InscriptionManagerView: View {
} }
} }
.searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites")) .searchable(text: $searchField, isPresented: $presentSearch, prompt: Text("Chercher parmi les équipes inscrites"))
.keyboardType(.alphabet)
.autocorrectionDisabled()
} }
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
_managementView() _managementView()
if testCreatedPlayers.isEmpty == false || pasteString != nil || editedTeam != nil { if createdPlayerIds.isEmpty == false || pasteString != nil || editedTeam != nil {
_buildingTeamView() _buildingTeamView()
} else if tournament.unsortedTeams().isEmpty { } else if tournament.unsortedTeams().isEmpty {
_inscriptionTipsView() _inscriptionTipsView()
@ -338,22 +345,29 @@ struct InscriptionManagerView: View {
_teamRegisteredView() _teamRegisteredView()
} }
} }
.sheet(isPresented: $presentPlayerSearch) { .sheet(isPresented: $presentPlayerSearch, onDismiss: {
selectionSearchField = nil
}) {
NavigationStack { NavigationStack {
SelectablePlayerListView(allowSelection: -1, filterOption: _filterOption()) { players in SelectablePlayerListView(allowSelection: -1, filterOption: _filterOption()) { players in
selectionSearchField = nil
players.forEach { player in players.forEach { player in
let newPlayer = PlayerRegistration(importedPlayer: player) let newPlayer = PlayerRegistration(importedPlayer: player)
newPlayer.setWeight(in: tournament) newPlayer.setWeight(in: tournament)
createdPlayers.insert(newPlayer) createdPlayers.insert(newPlayer)
testCreatedPlayers.insert(newPlayer.id) createdPlayerIds.insert(newPlayer.id)
} }
} contentUnavailableAction: { searchViewModel in
selectionSearchField = searchViewModel.searchText
presentPlayerSearch = false
presentPlayerCreation = true
} }
} }
} }
.sheet(isPresented: $presentPlayerCreation) { .sheet(isPresented: $presentPlayerCreation) {
PlayerPopoverView(sex: _addPlayerSex()) { p in PlayerPopoverView(source: _searchSource(), sex: _addPlayerSex()) { p in
createdPlayers.insert(p) createdPlayers.insert(p)
testCreatedPlayers.insert(p.id) createdPlayerIds.insert(p.id)
} }
} }
.sheet(isPresented: $presentImportView) { .sheet(isPresented: $presentImportView) {
@ -374,11 +388,11 @@ struct InscriptionManagerView: View {
} }
.toolbar { .toolbar {
if testCreatedPlayers.isEmpty == false { if createdPlayerIds.isEmpty == false {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button("Annuler", role: .cancel) { Button("Annuler", role: .cancel) {
createdPlayers.removeAll() createdPlayers.removeAll()
testCreatedPlayers.removeAll() createdPlayerIds.removeAll()
} }
} }
} }
@ -443,7 +457,7 @@ struct InscriptionManagerView: View {
} }
} }
} }
.navigationBarBackButtonHidden(testCreatedPlayers.isEmpty == false) .navigationBarBackButtonHidden(createdPlayerIds.isEmpty == false)
.toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.visible, for: .navigationBar)
.navigationTitle("Inscriptions") .navigationTitle("Inscriptions")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)

Loading…
Cancel
Save