git.iko.soy/dashared/hookah-tau
on develop help sign in
92 files changed+2755−493
since splitsnapshots
unifiedsplit
Hookah Tau.xcodeproj/project.pbxproj+116−18 Hookah Tau/API/APIClient.swift+2 Hookah Tau/API/ClientsService/ClientsService.swift+64−1 Hookah Tau/API/Settings/Resolvers/AddOtherAdminResolver.swift → Hookah Tau/API/ClientsService/Resolvers/AddOtherAdminResolver.swift Hookah Tau/API/ClientsService/Resolvers/BlocklistPutResolver.swift+33 Hookah Tau/API/ClientsService/Resolvers/ClientsListResolver.swift+1−1 Hookah Tau/API/ClientsService/Resolvers/DeleteOtherAdminResolver.swift+35 Hookah Tau/API/ClientsService/Resolvers/GetClientByPhoneNumberResolver.swift+35 Hookah Tau/API/Reservations api/ReservationsService.swift+62−3 Hookah Tau/API/Reservations api/Resolvers/Admin/AChangeReservationResolver.swift+34 Hookah Tau/API/Reservations api/Resolvers/Admin/ACreateReservationResolver.swift+37 Hookah Tau/API/Reservations api/Resolvers/Admin/ADeleteReservationResolver.swift+34 Hookah Tau/API/Reservations api/Resolvers/ReservationsAroundResolver.swift+35 Hookah Tau/API/ServerError.swift+1 Hookah Tau/API/Settings/SettingsService.swift−34 Hookah Tau/Components/PhoneTextField.swift+63 Hookah Tau/Controllers/Admin/Clients/Cells/ClientTableViewCell.swift+12−3 Hookah Tau/Controllers/Admin/Clients/ClientList.storyboard+110−63 Hookah Tau/Controllers/Admin/Clients/ClientsCoordinator.swift+1−1 Hookah Tau/Controllers/Admin/Clients/ClientsViewController.swift+175−8 Hookah Tau/Controllers/Admin/Clients/Profile/ClientProfileCoordinator.swift+25−2 Hookah Tau/Controllers/Admin/Clients/Profile/ProfileTableViewController.swift+181−22 Hookah Tau/Controllers/Admin/Reservations/AdminAddressCoordinator.swift+1 Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingCoordinator.swift+31 Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingViewController.swift+187 Hookah Tau/Controllers/Admin/Reservations/AdminReservations.storyboard+105 Hookah Tau/Controllers/Admin/Reservations/AdminReservationsController.swift−136 Hookah Tau/Controllers/Admin/Reservations/AdminReservationsCoordinator.swift−22 Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsController.swift+219 Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsCoordinator.swift+54 Hookah Tau/Controllers/Admin/Reservations/Cell/AdminReservationCellView.swift → Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.swift Hookah Tau/Controllers/Admin/Reservations/Cell/AdminReservationCellView.xib → Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.xib+11−21 Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/1/AdminFirstStepReservationViewCoordinator.swift+108 Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/2/AdminSecondStepReservationViewController.swift+124 Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/2/AdminSecondStepReservationViewCoordinator.swift+54 Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/3/AdminFinalStepReservationCoordinator.swift+47 Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.swift+298−7 Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.xib+121−35 Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.swift+5 Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.xib+1 Hookah Tau/Controllers/Admin/Tab bar/Admin.storyboard+13−8 Hookah Tau/Controllers/BaseTableViewController.swift+28 Hookah Tau/Controllers/BaseViewController.swift+1−1 Hookah Tau/Controllers/Client/Profile/ProfileChangeViewController.swift+3−3 Hookah Tau/Controllers/Client/Reservations/Active Reservations/ReservationsViewController.swift+23−2 Hookah Tau/Controllers/Client/Reservations/Maps/1/MapView1.xib+14−14 Hookah Tau/Controllers/Client/Reservations/Maps/MapView.swift+1 Hookah Tau/Controllers/Client/Reservations/Reservation.storyboard+4−4 Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepReservationCoordinator.swift+8−1 Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepReservationViewController.swift+15−6 Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepView.xib+18−18 Hookah Tau/Controllers/Client/Reservations/Steps/2/SecondStepView.swift−7 Hookah Tau/Controllers/Client/Reservations/Steps/2/SecondStepView.xib+12−19 Hookah Tau/Controllers/Client/Reservations/Steps/3/ThirdStepReservationViewController.swift+1−1 Hookah Tau/Controllers/Client/Reservations/Steps/3/ThirdStepView.xib+3−3 Hookah Tau/Controllers/Client/Reservations/Steps/change /ChangeReservationViewController.swift+1 Hookah Tau/Controllers/Client/Tab bar/Client.storyboard+10−6 Hookah Tau/HFunc.swift+7 Hookah Tau/Info.plist+5−3 Hookah Tau/Models/Reservation.swift+24−1 Hookah Tau/Models/User.swift+19−1 Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.png Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json+36−18 Hookah Tau/Supporting Files/Assets.xcassets/admin.imageset/Contents.json+12 Hookah Tau/Supporting Files/Assets.xcassets/admin.imageset/admin.pdf Hookah Tau/Supporting Files/Assets.xcassets/blacklist.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/blacklist.imageset/blacklist.pdf Hookah Tau/Supporting Files/Assets.xcassets/peoplee.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/peoplee.imageset/peoplee.pdf Hookah Tau/Supporting Files/Assets.xcassets/person.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/person.imageset/person.pdf Hookah Tau/Supporting Files/Assets.xcassets/stack.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/stack.imageset/Light-S.pdf Hookah Tau/Supporting Files/Assets.xcassets/stack2.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/stack2.imageset/stack2.pdf
Hookah Tau.xcodeproj/project.pbxproj+116−18
@@ -17,6 +17,9 @@
1717 12118B9E23682049006BAEE3 /* PhoneCodeResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12118B9D23682049006BAEE3 /* PhoneCodeResolver.swift */; };
1818 1212048C23914CFD00CAF162 /* DeleteReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1212048B23914CFD00CAF162 /* DeleteReservationResolver.swift */; };
1919 1212048E23915A0A00CAF162 /* ChangeReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1212048D23915A0A00CAF162 /* ChangeReservationResolver.swift */; };
20+ 1220A6F423CDB1AD00365E36 /* GetClientByPhoneNumberResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220A6F323CDB1AD00365E36 /* GetClientByPhoneNumberResolver.swift */; };
21+ 1220A6F723CDB64A00365E36 /* AdminSecondStepReservationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220A6F623CDB64A00365E36 /* AdminSecondStepReservationViewController.swift */; };
22+ 1220A6FA23CDBE7E00365E36 /* AdminFinalStepReservationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1220A6F923CDBE7E00365E36 /* AdminFinalStepReservationCoordinator.swift */; };
2023 12219CEF2353A63F0057DDEE /* ReservationsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12219CEE2353A63E0057DDEE /* ReservationsCoordinator.swift */; };
2124 12230AEA238FD8B3004A01E5 /* MapView2.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12230AE9238FD8B3004A01E5 /* MapView2.xib */; };
2225 12230AEC238FD8C5004A01E5 /* MapView2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12230AEB238FD8C5004A01E5 /* MapView2.swift */; };
@@ -40,6 +43,7 @@
4043 124D007C23721FD400AE76B9 /* Reservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124D007B23721FD400AE76B9 /* Reservation.swift */; };
4144 124D007F237222B200AE76B9 /* ReservationsResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124D007E237222B200AE76B9 /* ReservationsResolver.swift */; };
4245 124D0081237222C900AE76B9 /* ReservationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 124D0080237222C900AE76B9 /* ReservationsService.swift */; };
46+ 1252D9E523CDCDB300446B6F /* ACreateReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1252D9E423CDCDB300446B6F /* ACreateReservationResolver.swift */; };
4347 125AE51D2395583E00F847BD /* ChangeReservationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125AE51C2395583E00F847BD /* ChangeReservationCoordinator.swift */; };
4448 125AE51F2395585300F847BD /* ChangeReservationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125AE51E2395585300F847BD /* ChangeReservationViewController.swift */; };
4549 125AE5212395753400F847BD /* HFunc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 125AE5202395753400F847BD /* HFunc.swift */; };
@@ -51,6 +55,9 @@
5155 12838EB4236779C700CEF108 /* ApiResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12838EB3236779C700CEF108 /* ApiResolver.swift */; };
5256 12838EB623677FB900CEF108 /* ApiRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12838EB523677FB900CEF108 /* ApiRequest.swift */; };
5357 12838EB82367834900CEF108 /* MyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12838EB72367834900CEF108 /* MyCodable.swift */; };
58+ 129F162723CC838200EE13FC /* AdminFirstStepReservationViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129F162623CC838200EE13FC /* AdminFirstStepReservationViewCoordinator.swift */; };
59+ 129F162C23CC8D2E00EE13FC /* AdminSecondStepReservationViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129F162B23CC8D2E00EE13FC /* AdminSecondStepReservationViewCoordinator.swift */; };
60+ 129F163023CC8EF600EE13FC /* PhoneTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 129F162F23CC8EF600EE13FC /* PhoneTextField.swift */; };
5461 12A075F72373535C00D749BB /* FirstStepReservationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A075F62373535C00D749BB /* FirstStepReservationViewController.swift */; };
5562 12A075F9237353F500D749BB /* MapView1.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12A075F8237353F500D749BB /* MapView1.xib */; };
5663 12A075FB2373561A00D749BB /* Reservation.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12A075FA2373561A00D749BB /* Reservation.storyboard */; };
@@ -69,8 +76,11 @@
6976 12B3A7AF23AC1872009E2FA6 /* AdminNoReservationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7AE23AC1872009E2FA6 /* AdminNoReservationsView.swift */; };
7077 12B3A7B123AC1885009E2FA6 /* AdminNoReservationsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A7B023AC1885009E2FA6 /* AdminNoReservationsView.xib */; };
7178 12B3A7B423AC1D09009E2FA6 /* AdminReservationCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A7B323AC1D09009E2FA6 /* AdminReservationCellView.xib */; };
72− 12B3A7B723ACCD07009E2FA6 /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */; };
7379 12B3A7B923ACCD83009E2FA6 /* AddOtherAdminResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */; };
80+ 12B3A7BC23ACFD75009E2FA6 /* ReservationsAroundResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */; };
81+ 12B3A7BE23AD0049009E2FA6 /* AdminReservationEditingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */; };
82+ 12B3A7C023AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */; };
83+ 12B3A7C423AD2FCD009E2FA6 /* AChangeReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */; };
7484 12B4447C238EF25C000B6B16 /* CreateReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */; };
7585 12B6A60F235B857A00898B31 /* ProfileChangeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6A60E235B857A00898B31 /* ProfileChangeCoordinator.swift */; };
7686 12B6A611235B8EC500898B31 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6A610235B8EC500898B31 /* User.swift */; };
@@ -91,6 +101,9 @@
91101 12D8FA2D237F003800305F57 /* DateView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12D8FA2C237F003800305F57 /* DateView.xib */; };
92102 12D8FA2F237F00E200305F57 /* DateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D8FA2E237F00E200305F57 /* DateView.swift */; };
93103 12D8FA31237F08C700305F57 /* AllReservations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D8FA30237F08C700305F57 /* AllReservations.swift */; };
104+ 12D928AF23C47DCF0051724D /* ADeleteReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D928AE23C47DCF0051724D /* ADeleteReservationResolver.swift */; };
105+ 12D928B123C486380051724D /* BaseTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D928B023C486380051724D /* BaseTableViewController.swift */; };
106+ 12D928B323C7464B0051724D /* BlocklistPutResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D928B223C7464B0051724D /* BlocklistPutResolver.swift */; };
94107 12DBEEB223539F2100AF1BB6 /* Client.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12DBEEB123539F2100AF1BB6 /* Client.storyboard */; };
95108 12E35071239FA8CA004BB188 /* Extensions+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E35070239FA8CA004BB188 /* Extensions+String.swift */; };
96109 12E35074239FAF52004BB188 /* ClientsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E35073239FAF52004BB188 /* ClientsService.swift */; };
@@ -108,6 +121,7 @@
108121 12F9652A23868E6000FCA696 /* SecondStepView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12F9652923868E6000FCA696 /* SecondStepView.xib */; };
109122 12F9652C23868E7200FCA696 /* SecondStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F9652B23868E7200FCA696 /* SecondStepView.swift */; };
110123 12F9652E23869F3100FCA696 /* SecondStepReservationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12F9652D23869F3100FCA696 /* SecondStepReservationCoordinator.swift */; };
124+ 12FC07B923C8D17600EDC55B /* DeleteOtherAdminResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FC07B823C8D17600EDC55B /* DeleteOtherAdminResolver.swift */; };
111125 12FC40382382C93D00AB593B /* TableButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FC40372382C93C00AB593B /* TableButtonStyle.swift */; };
112126 12FC403A2382CB6F00AB593B /* TableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12FC40392382CB6F00AB593B /* TableButton.swift */; };
113127 F30369B5233679EA00A8EA84 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F30369B4233679EA00A8EA84 /* LaunchScreen.storyboard */; };
@@ -168,6 +182,9 @@
168182 12118B9D23682049006BAEE3 /* PhoneCodeResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneCodeResolver.swift; sourceTree = "<group>"; };
169183 1212048B23914CFD00CAF162 /* DeleteReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteReservationResolver.swift; sourceTree = "<group>"; };
170184 1212048D23915A0A00CAF162 /* ChangeReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeReservationResolver.swift; sourceTree = "<group>"; };
185+ 1220A6F323CDB1AD00365E36 /* GetClientByPhoneNumberResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetClientByPhoneNumberResolver.swift; sourceTree = "<group>"; };
186+ 1220A6F623CDB64A00365E36 /* AdminSecondStepReservationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdminSecondStepReservationViewController.swift; sourceTree = "<group>"; };
187+ 1220A6F923CDBE7E00365E36 /* AdminFinalStepReservationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminFinalStepReservationCoordinator.swift; sourceTree = "<group>"; };
171188 12219CEE2353A63E0057DDEE /* ReservationsCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReservationsCoordinator.swift; sourceTree = "<group>"; };
172189 12230AE9238FD8B3004A01E5 /* MapView2.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapView2.xib; sourceTree = "<group>"; };
173190 12230AEB238FD8C5004A01E5 /* MapView2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView2.swift; sourceTree = "<group>"; };
@@ -191,6 +208,7 @@
191208 124D007B23721FD400AE76B9 /* Reservation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reservation.swift; sourceTree = "<group>"; };
192209 124D007E237222B200AE76B9 /* ReservationsResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationsResolver.swift; sourceTree = "<group>"; };
193210 124D0080237222C900AE76B9 /* ReservationsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationsService.swift; sourceTree = "<group>"; };
211+ 1252D9E423CDCDB300446B6F /* ACreateReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACreateReservationResolver.swift; sourceTree = "<group>"; };
194212 125AE51C2395583E00F847BD /* ChangeReservationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeReservationCoordinator.swift; sourceTree = "<group>"; };
195213 125AE51E2395585300F847BD /* ChangeReservationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeReservationViewController.swift; sourceTree = "<group>"; };
196214 125AE5202395753400F847BD /* HFunc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HFunc.swift; sourceTree = "<group>"; };
@@ -201,6 +219,9 @@
201219 12838EB3236779C700CEF108 /* ApiResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiResolver.swift; sourceTree = "<group>"; };
202220 12838EB523677FB900CEF108 /* ApiRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiRequest.swift; sourceTree = "<group>"; };
203221 12838EB72367834900CEF108 /* MyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCodable.swift; sourceTree = "<group>"; };
222+ 129F162623CC838200EE13FC /* AdminFirstStepReservationViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminFirstStepReservationViewCoordinator.swift; sourceTree = "<group>"; };
223+ 129F162B23CC8D2E00EE13FC /* AdminSecondStepReservationViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminSecondStepReservationViewCoordinator.swift; sourceTree = "<group>"; };
224+ 129F162F23CC8EF600EE13FC /* PhoneTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneTextField.swift; sourceTree = "<group>"; };
204225 12A075F62373535C00D749BB /* FirstStepReservationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstStepReservationViewController.swift; sourceTree = "<group>"; };
205226 12A075F8237353F500D749BB /* MapView1.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MapView1.xib; sourceTree = "<group>"; };
206227 12A075FA2373561A00D749BB /* Reservation.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Reservation.storyboard; sourceTree = "<group>"; };
@@ -219,8 +240,11 @@
219240 12B3A7AE23AC1872009E2FA6 /* AdminNoReservationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminNoReservationsView.swift; sourceTree = "<group>"; };
220241 12B3A7B023AC1885009E2FA6 /* AdminNoReservationsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdminNoReservationsView.xib; sourceTree = "<group>"; };
221242 12B3A7B323AC1D09009E2FA6 /* AdminReservationCellView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdminReservationCellView.xib; sourceTree = "<group>"; };
222− 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = "<group>"; };
223243 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOtherAdminResolver.swift; sourceTree = "<group>"; };
244+ 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationsAroundResolver.swift; sourceTree = "<group>"; };
245+ 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationEditingViewController.swift; sourceTree = "<group>"; };
246+ 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationEditingCoordinator.swift; sourceTree = "<group>"; };
247+ 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AChangeReservationResolver.swift; sourceTree = "<group>"; };
224248 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateReservationResolver.swift; sourceTree = "<group>"; };
225249 12B6A60E235B857A00898B31 /* ProfileChangeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileChangeCoordinator.swift; sourceTree = "<group>"; };
226250 12B6A610235B8EC500898B31 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
@@ -241,6 +265,9 @@
241265 12D8FA2C237F003800305F57 /* DateView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DateView.xib; sourceTree = "<group>"; };
242266 12D8FA2E237F00E200305F57 /* DateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateView.swift; sourceTree = "<group>"; };
243267 12D8FA30237F08C700305F57 /* AllReservations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllReservations.swift; sourceTree = "<group>"; };
268+ 12D928AE23C47DCF0051724D /* ADeleteReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ADeleteReservationResolver.swift; sourceTree = "<group>"; };
269+ 12D928B023C486380051724D /* BaseTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewController.swift; sourceTree = "<group>"; };
270+ 12D928B223C7464B0051724D /* BlocklistPutResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlocklistPutResolver.swift; sourceTree = "<group>"; };
244271 12DBEEB123539F2100AF1BB6 /* Client.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Client.storyboard; sourceTree = "<group>"; };
245272 12E35070239FA8CA004BB188 /* Extensions+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extensions+String.swift"; sourceTree = "<group>"; };
246273 12E35073239FAF52004BB188 /* ClientsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientsService.swift; sourceTree = "<group>"; };
@@ -258,6 +285,7 @@
258285 12F9652923868E6000FCA696 /* SecondStepView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SecondStepView.xib; sourceTree = "<group>"; };
259286 12F9652B23868E7200FCA696 /* SecondStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondStepView.swift; sourceTree = "<group>"; };
260287 12F9652D23869F3100FCA696 /* SecondStepReservationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondStepReservationCoordinator.swift; sourceTree = "<group>"; };
288+ 12FC07B823C8D17600EDC55B /* DeleteOtherAdminResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteOtherAdminResolver.swift; sourceTree = "<group>"; };
261289 12FC40372382C93C00AB593B /* TableButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableButtonStyle.swift; sourceTree = "<group>"; };
262290 12FC40392382CB6F00AB593B /* TableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableButton.swift; sourceTree = "<group>"; };
263291 F30369B4233679EA00A8EA84 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -378,6 +406,31 @@
378406 path = Reservations;
379407 sourceTree = "<group>";
380408 };
409+ 1220A6F523CDB63000365E36 /* 2 */ = {
410+ isa = PBXGroup;
411+ children = (
412+ 1220A6F623CDB64A00365E36 /* AdminSecondStepReservationViewController.swift */,
413+ 129F162B23CC8D2E00EE13FC /* AdminSecondStepReservationViewCoordinator.swift */,
414+ );
415+ path = 2;
416+ sourceTree = "<group>";
417+ };
418+ 1220A6F823CDB65C00365E36 /* 1 */ = {
419+ isa = PBXGroup;
420+ children = (
421+ 129F162623CC838200EE13FC /* AdminFirstStepReservationViewCoordinator.swift */,
422+ );
423+ path = 1;
424+ sourceTree = "<group>";
425+ };
426+ 1220A6FB23CDBED900365E36 /* 3 */ = {
427+ isa = PBXGroup;
428+ children = (
429+ 1220A6F923CDBE7E00365E36 /* AdminFinalStepReservationCoordinator.swift */,
430+ );
431+ path = 3;
432+ sourceTree = "<group>";
433+ };
381434 12230AED238FD8D5004A01E5 /* 1 */ = {
382435 isa = PBXGroup;
383436 children = (
@@ -454,11 +507,11 @@
454507 123D25ED239E3D50002FA32B /* Reservations */ = {
455508 isa = PBXGroup;
456509 children = (
510+ 129F162523CC836600EE13FC /* Making a reeservation */,
457511 12B3A79623ABAD9A009E2FA6 /* Views */,
458− 12B3A7A623ABDD92009E2FA6 /* Cell */,
459− 12B3A7A223ABD594009E2FA6 /* AdminReservationsController.swift */,
512+ 12B3A7C123AD0751009E2FA6 /* Editing */,
513+ 12B3A7C223AD0762009E2FA6 /* List of reservations */,
460514 123D25EF239E3E11002FA32B /* AdminAddressCoordinator.swift */,
461− 12B3A7A423ABDA25009E2FA6 /* AdminReservationsCoordinator.swift */,
462515 12B3A7A923ABDDC1009E2FA6 /* AdminReservations.storyboard */,
463516 );
464517 path = Reservations;
@@ -519,6 +572,7 @@
519572 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */,
520573 1212048B23914CFD00CAF162 /* DeleteReservationResolver.swift */,
521574 1212048D23915A0A00CAF162 /* ChangeReservationResolver.swift */,
575+ 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */,
522576 );
523577 path = Resolvers;
524578 sourceTree = "<group>";
@@ -535,7 +589,6 @@
535589 12838EB923679AEB00CEF108 /* API */ = {
536590 isa = PBXGroup;
537591 children = (
538− 12B3A7B523ACCCD0009E2FA6 /* Settings */,
539592 12838EA8236764A600CEF108 /* APIClient.swift */,
540593 12838EB3236779C700CEF108 /* ApiResolver.swift */,
541594 12838EAF236777D400CEF108 /* ServerError.swift */,
@@ -549,9 +602,27 @@
549602 path = API;
550603 sourceTree = "<group>";
551604 };
605+ 129F162523CC836600EE13FC /* Making a reeservation */ = {
606+ isa = PBXGroup;
607+ children = (
608+ 1220A6F823CDB65C00365E36 /* 1 */,
609+ 1220A6F523CDB63000365E36 /* 2 */,
610+ 1220A6FB23CDBED900365E36 /* 3 */,
611+ );
612+ path = "Making a reeservation";
613+ sourceTree = "<group>";
614+ };
615+ 129F162823CC89EA00EE13FC /* Phone number */ = {
616+ isa = PBXGroup;
617+ children = (
618+ );
619+ path = "Phone number";
620+ sourceTree = "<group>";
621+ };
552622 12B3A79623ABAD9A009E2FA6 /* Views */ = {
553623 isa = PBXGroup;
554624 children = (
625+ 129F162823CC89EA00EE13FC /* Phone number */,
555626 12B3A7B223AC1A1F009E2FA6 /* No reserv */,
556627 12B3A79723ABADB9009E2FA6 /* AdminReservationView.xib */,
557628 12B3A79E23ABCA21009E2FA6 /* AdminReservationView.swift */,
@@ -581,6 +652,9 @@
581652 isa = PBXGroup;
582653 children = (
583654 12B3A7AC23ABEABF009E2FA6 /* AReservationsResolver.swift */,
655+ 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */,
656+ 12D928AE23C47DCF0051724D /* ADeleteReservationResolver.swift */,
657+ 1252D9E423CDCDB300446B6F /* ACreateReservationResolver.swift */,
584658 );
585659 path = Admin;
586660 sourceTree = "<group>";
@@ -594,21 +668,23 @@
594668 path = "No reserv";
595669 sourceTree = "<group>";
596670 };
597− 12B3A7B523ACCCD0009E2FA6 /* Settings */ = {
671+ 12B3A7C123AD0751009E2FA6 /* Editing */ = {
598672 isa = PBXGroup;
599673 children = (
600− 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */,
601− 12B3A7BA23ACCD88009E2FA6 /* Resolvers */,
674+ 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */,
675+ 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */,
602676 );
603− path = Settings;
677+ name = Editing;
604678 sourceTree = "<group>";
605679 };
606− 12B3A7BA23ACCD88009E2FA6 /* Resolvers */ = {
680+ 12B3A7C223AD0762009E2FA6 /* List of reservations */ = {
607681 isa = PBXGroup;
608682 children = (
609− 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */,
683+ 12B3A7A623ABDD92009E2FA6 /* Cell */,
684+ 12B3A7A223ABD594009E2FA6 /* AdminReservationsController.swift */,
685+ 12B3A7A423ABDA25009E2FA6 /* AdminReservationsCoordinator.swift */,
610686 );
611− path = Resolvers;
687+ path = "List of reservations";
612688 sourceTree = "<group>";
613689 };
614690 12BD95AE237EBE52009614D8 /* Maps */ = {
@@ -676,8 +752,12 @@
676752 12E35075239FAF8F004BB188 /* Resolvers */ = {
677753 isa = PBXGroup;
678754 children = (
755+ 12FC07B823C8D17600EDC55B /* DeleteOtherAdminResolver.swift */,
756+ 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */,
679757 12E35077239FAFA4004BB188 /* ClientsListResolver.swift */,
680758 12E3507C239FD1F3004BB188 /* ClientReservationsResolver.swift */,
759+ 12D928B223C7464B0051724D /* BlocklistPutResolver.swift */,
760+ 1220A6F323CDB1AD00365E36 /* GetClientByPhoneNumberResolver.swift */,
681761 );
682762 path = Resolvers;
683763 sourceTree = "<group>";
@@ -764,6 +844,7 @@
764844 F3FC7DFC2315C4710080D851 /* BaseCoordinator.swift */,
765845 F3FC7DFA2315C25D0080D851 /* ApplicationCoordinator.swift */,
766846 12B6A612235B9B4300898B31 /* BaseViewController.swift */,
847+ 12D928B023C486380051724D /* BaseTableViewController.swift */,
767848 12B3A79D23ABB8F4009E2FA6 /* Picker view */,
768849 F30369BB2336845F00A8EA84 /* Admin */,
769850 F30369BA2336845400A8EA84 /* Client */,
@@ -791,6 +872,7 @@
791872 F3DC5A04233E89D500B5E751 /* Button.swift */,
792873 12FC40372382C93C00AB593B /* TableButtonStyle.swift */,
793874 12FC40392382CB6F00AB593B /* TableButton.swift */,
875+ 129F162F23CC8EF600EE13FC /* PhoneTextField.swift */,
794876 );
795877 path = Components;
796878 sourceTree = "<group>";
@@ -1115,11 +1197,14 @@
11151197 files = (
11161198 12EA99482362B684005D9FE9 /* AddresViewController.swift in Sources */,
11171199 124D007F237222B200AE76B9 /* ReservationsResolver.swift in Sources */,
1200+ 129F163023CC8EF600EE13FC /* PhoneTextField.swift in Sources */,
1201+ 129F162C23CC8D2E00EE13FC /* AdminSecondStepReservationViewCoordinator.swift in Sources */,
11181202 12230AF723900D44004A01E5 /* EstablishmentResolver.swift in Sources */,
11191203 1246B719235CD3AD004CBFA1 /* NoReservationsView.swift in Sources */,
11201204 F3895F4A233D323700FA6A37 /* CodeCoordinator.swift in Sources */,
11211205 12B3A7B923ACCD83009E2FA6 /* AddOtherAdminResolver.swift in Sources */,
11221206 12E3507F239FDFC2004BB188 /* ClientProfileCoordinator.swift in Sources */,
1207+ 12D928B323C7464B0051724D /* BlocklistPutResolver.swift in Sources */,
11231208 12838EB4236779C700CEF108 /* ApiResolver.swift in Sources */,
11241209 12838EA9236764A600CEF108 /* APIClient.swift in Sources */,
11251210 123615D12355141600D1122A /* ProfileView.swift in Sources */,
@@ -1140,12 +1225,15 @@
11401225 12838EB82367834900CEF108 /* MyCodable.swift in Sources */,
11411226 F3DC5A05233E89D500B5E751 /* Button.swift in Sources */,
11421227 F3FC7DFB2315C25D0080D851 /* ApplicationCoordinator.swift in Sources */,
1228+ 12B3A7BE23AD0049009E2FA6 /* AdminReservationEditingViewController.swift in Sources */,
11431229 12BE4B352399A60A00FC974B /* AdminTabbarController.swift in Sources */,
11441230 12FC403A2382CB6F00AB593B /* TableButton.swift in Sources */,
1231+ 12D928AF23C47DCF0051724D /* ADeleteReservationResolver.swift in Sources */,
11451232 12EB698123661CB300E4D1CE /* AddressCell.swift in Sources */,
11461233 F3895F48233D2EEA00FA6A37 /* CodeSmsViewController.swift in Sources */,
11471234 12219CEF2353A63F0057DDEE /* ReservationsCoordinator.swift in Sources */,
11481235 12230AF423900BCB004A01E5 /* EstablishmentService.swift in Sources */,
1236+ 129F162723CC838200EE13FC /* AdminFirstStepReservationViewCoordinator.swift in Sources */,
11491237 12A07601237359BB00D749BB /* MapView1.swift in Sources */,
11501238 125AE5212395753400F847BD /* HFunc.swift in Sources */,
11511239 12BD95B8237EDB01009614D8 /* TimePointView.swift in Sources */,
@@ -1171,27 +1259,34 @@
11711259 12B3A7A323ABD594009E2FA6 /* AdminReservationsController.swift in Sources */,
11721260 F3FC7DCD2315BE610080D851 /* AppDelegate.swift in Sources */,
11731261 12838EB623677FB900CEF108 /* ApiRequest.swift in Sources */,
1262+ 1220A6FA23CDBE7E00365E36 /* AdminFinalStepReservationCoordinator.swift in Sources */,
11741263 12118B9E23682049006BAEE3 /* PhoneCodeResolver.swift in Sources */,
11751264 F30369BE2336924D00A8EA84 /* ClientTabbarCoordinator.swift in Sources */,
11761265 F3FC7DFD2315C4710080D851 /* BaseCoordinator.swift in Sources */,
1266+ 1220A6F723CDB64A00365E36 /* AdminSecondStepReservationViewController.swift in Sources */,
11771267 12E35071239FA8CA004BB188 /* Extensions+String.swift in Sources */,
1268+ 12FC07B923C8D17600EDC55B /* DeleteOtherAdminResolver.swift in Sources */,
11781269 F3DC59FF233E893400B5E751 /* Extensions+UIFont.swift in Sources */,
11791270 F3895F53233D5EEB00FA6A37 /* BlackButtonStyle.swift in Sources */,
11801271 12F9652C23868E7200FCA696 /* SecondStepView.swift in Sources */,
11811272 120BFC7923891942001EBE89 /* ThirdStepReservationCoordinator.swift in Sources */,
1273+ 12B3A7BC23ACFD75009E2FA6 /* ReservationsAroundResolver.swift in Sources */,
11821274 12118B9C23682022006BAEE3 /* RegistrationResolver.swift in Sources */,
1275+ 12B3A7C423AD2FCD009E2FA6 /* AChangeReservationResolver.swift in Sources */,
11831276 F30369C02336925800A8EA84 /* ClientTabbarController.swift in Sources */,
1277+ 12B3A7C023AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift in Sources */,
11841278 F31E20C72348DDB900BEA215 /* Extensions+UIView.swift in Sources */,
11851279 F3851B302340A513009EE0B6 /* RegistationViewModel.swift in Sources */,
11861280 12B3A7AF23AC1872009E2FA6 /* AdminNoReservationsView.swift in Sources */,
11871281 12838EAE2367701300CEF108 /* AuthorizationService.swift in Sources */,
1188− 12B3A7B723ACCD07009E2FA6 /* SettingsService.swift in Sources */,
11891282 12C72EDF23537D1B00AD7004 /* ProfileCoordinator.swift in Sources */,
11901283 12E3507D239FD1F3004BB188 /* ClientReservationsResolver.swift in Sources */,
11911284 12EA994E2362FCE8005D9FE9 /* AddressView.swift in Sources */,
1285+ 1220A6F423CDB1AD00365E36 /* GetClientByPhoneNumberResolver.swift in Sources */,
11921286 F3895F4C233D347E00FA6A37 /* PhoneNumberViewController.swift in Sources */,
11931287 12EE4A76234BB8FE005B4D1A /* CodeView.swift in Sources */,
11941288 12230AF123900974004A01E5 /* TotalStorage.swift in Sources */,
1289+ 1252D9E523CDCDB300446B6F /* ACreateReservationResolver.swift in Sources */,
11951290 12BD95B2237EC22B009614D8 /* FirstStepView.swift in Sources */,
11961291 F3EBE2472340FE87000FEE1A /* NameTextView.swift in Sources */,
11971292 120BFC7723891925001EBE89 /* ThirdStepReservationViewController.swift in Sources */,
@@ -1204,6 +1299,7 @@
12041299 1212048E23915A0A00CAF162 /* ChangeReservationResolver.swift in Sources */,
12051300 12230AFC239034D3004A01E5 /* SecondStepReservationViewController.swift in Sources */,
12061301 F3851B2923409F8A009EE0B6 /* PhoneView.swift in Sources */,
1302+ 12D928B123C486380051724D /* BaseTableViewController.swift in Sources */,
12071303 12C72EE3235380EB00AD7004 /* ReservationsViewController.swift in Sources */,
12081304 12B6A611235B8EC500898B31 /* User.swift in Sources */,
12091305 12B3A7AD23ABEABF009E2FA6 /* AReservationsResolver.swift in Sources */,
@@ -1370,14 +1466,15 @@
13701466 buildSettings = {
13711467 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
13721468 CODE_SIGN_STYLE = Automatic;
1373− DEVELOPMENT_TEAM = 9Z6LFGKA9G;
1469+ CURRENT_PROJECT_VERSION = 5;
1470+ DEVELOPMENT_TEAM = 23S94LRHD8;
13741471 INFOPLIST_FILE = "Hookah Tau/Info.plist";
13751472 IPHONEOS_DEPLOYMENT_TARGET = 11.0;
13761473 LD_RUNPATH_SEARCH_PATHS = (
13771474 "$(inherited)",
13781475 "@executable_path/Frameworks",
13791476 );
1380− PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau";
1477+ PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau1";
13811478 PRODUCT_NAME = "$(TARGET_NAME)";
13821479 SWIFT_VERSION = 5.0;
13831480 TARGETED_DEVICE_FAMILY = "1,2";
@@ -1389,14 +1486,15 @@
13891486 buildSettings = {
13901487 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
13911488 CODE_SIGN_STYLE = Automatic;
1392− DEVELOPMENT_TEAM = 9Z6LFGKA9G;
1489+ CURRENT_PROJECT_VERSION = 5;
1490+ DEVELOPMENT_TEAM = 23S94LRHD8;
13931491 INFOPLIST_FILE = "Hookah Tau/Info.plist";
13941492 IPHONEOS_DEPLOYMENT_TARGET = 11.0;
13951493 LD_RUNPATH_SEARCH_PATHS = (
13961494 "$(inherited)",
13971495 "@executable_path/Frameworks",
13981496 );
1399− PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau";
1497+ PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau1";
14001498 PRODUCT_NAME = "$(TARGET_NAME)";
14011499 SWIFT_VERSION = 5.0;
14021500 TARGETED_DEVICE_FAMILY = "1,2";
Hookah Tau/API/APIClient.swift+2
@@ -48,6 +48,8 @@ class APIClient {
4848 let json = String(data: data, encoding: String.Encoding.utf8)
4949 print("Failure Response: \(String(describing: json))")
5050 }
51+ case 404:
52+ return GeneralError.notFound
5153 case 422:
5254 if let data = response.data, let error = SE.fromJSONToSelf(data: data) {
5355 print(error)
Hookah Tau/API/ClientsService/ClientsService.swift+64−1
@@ -15,7 +15,7 @@ class ClientsService {
1515 self.apiClient = apiClient
1616 }
1717
18− func loadClientsList(completion: @escaping ((Result<[FullUser], GeneralError>) -> Void)) {
18+ func loadClientsList(completion: @escaping ((Result<[Client], GeneralError>) -> Void)) {
1919 let resolver = ClientsListResolver<ClientsResponce>()
2020 let request = ApiRequest(resolver: resolver, httpMethod: .get)
2121
@@ -63,4 +63,67 @@ class ClientsService {
6363 }
6464
6565 }
66+
67+ /// Метод для удаления или добавления пользователя в чс
68+ func changeBlockList(whatToDo: CrudMethod, phone: String, completion: @escaping ((Bool) -> Void)) {
69+ let resolver = BlocklistPutResolver(blockList: [phone])
70+ let request = ApiRequest(resolver: resolver, httpMethod: whatToDo)
71+
72+ apiClient.load(request: request.request) { (res) in
73+ switch res {
74+ case .failure:
75+ completion(false)
76+ return
77+ case .success:
78+ completion(true)
79+ return
80+ }
81+ }
82+ }
83+
84+ /// Метод для изменения статуса админки
85+ func changeAdmin(crud: CrudMethod, data: String, completion: @escaping ((Bool) -> Void)) {
86+ var resolver: ApiResolver!
87+ if crud == .put {
88+ resolver = AddOtherAdminResolver(phone: data)
89+ } else {
90+ resolver = DeleteOtherAdminResolver(uuid: data)
91+ }
92+
93+ let request = ApiRequest(resolver: resolver, httpMethod: crud)
94+
95+ apiClient.load(request: request.request) { result in
96+ switch result {
97+ case .failure:
98+ completion(false)
99+ return
100+ case .success:
101+ completion(true)
102+ return
103+ }
104+ }
105+ }
106+
107+ func getClientByPhoneNumber(userPhone: String, completion: @escaping ((Result<FullUser, GeneralError>) -> Void)) {
108+ let resolver = GetClientByPhoneNumberResolver<ClientInResponce>(userPhone: userPhone)
109+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
110+
111+ apiClient.load(request: request.request) { (result) in
112+ switch result {
113+ case .failure(let err):
114+ completion(.failure(err))
115+ return
116+ case .success(let responce):
117+ guard
118+ let d = responce.data,
119+ let decData = resolver.targetClass().fromJSONToSelf(data: d)
120+ else {
121+ completion(.failure(GeneralError.decodeError))
122+ return
123+ }
124+
125+ completion(.success(decData.client))
126+ }
127+ }
128+ }
66129}
Hookah Tau/API/Settings/Resolvers/AddOtherAdminResolver.swift → Hookah Tau/API/ClientsService/Resolvers/AddOtherAdminResolver.swiftrenamed

The content is the same.

Hookah Tau/API/ClientsService/Resolvers/BlocklistPutResolver.swiftnew file+33
@@ -0,0 +1,33 @@
1+//
2+// BlocklistPutResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 09/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct RequestBlocklist: MyCodable {
12+ var blockList: [String]
13+}
14+
15+class BlocklistPutResolver: ApiResolver {
16+ var name: String {
17+ return "/blocklist"
18+ }
19+
20+ var groupName: String? {
21+ return "/admin"
22+ }
23+
24+ var blockList: [String]
25+
26+ init(blockList: [String]) {
27+ self.blockList = blockList
28+ }
29+
30+ func parameters() -> MyCodable? {
31+ return RequestBlocklist(blockList: blockList)
32+ }
33+}
Hookah Tau/API/ClientsService/Resolvers/ClientsListResolver.swift+1−1
@@ -9,7 +9,7 @@
99import Foundation
1010
1111struct ClientsResponce: MyCodable {
12− var clients: [FullUser]
12+ var clients: [Client]
1313}
1414
1515class ClientsListResolver<Responce: MyCodable>: ApiResolver {
Hookah Tau/API/ClientsService/Resolvers/DeleteOtherAdminResolver.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// DeleteOtherAdminResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+
12+class DeleteOtherAdminResolver: ApiResolver {
13+
14+ var name: String {
15+ return "/other"
16+ }
17+
18+ var groupName: String? {
19+ return "/admin"
20+ }
21+
22+ struct Request: MyCodable {
23+ let badAdmin: String
24+ }
25+
26+ let uuid: String
27+
28+ init(uuid: String) {
29+ self.uuid = uuid
30+ }
31+
32+ func parameters() -> MyCodable? {
33+ return Request(badAdmin: self.uuid)
34+ }
35+}
Hookah Tau/API/ClientsService/Resolvers/GetClientByPhoneNumberResolver.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// GetClientByPhoneNumberResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 14/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct ClientInResponce: MyCodable {
12+ var client: FullUser
13+}
14+
15+/// `GET /admin/clients/by-phone/{user id}`
16+class GetClientByPhoneNumberResolver<Responce: MyCodable>: ApiResolver {
17+
18+ var name: String {
19+ return "/clients/by-phone/\(userPhone)"
20+ }
21+
22+ var groupName: String? {
23+ return "/admin"
24+ }
25+
26+ var userPhone: String
27+
28+ init(userPhone: String) {
29+ self.userPhone = userPhone
30+ }
31+
32+ func targetClass() -> Responce.Type {
33+ return Responce.self
34+ }
35+}
Hookah Tau/API/Reservations api/ReservationsService.swift+62−3
@@ -40,8 +40,14 @@ class ReservationsService {
4040 }
4141 }
4242
43− func deleteReservation(uuid: String, completion: @escaping ((Bool) -> Void)) {
44− let resolver = DeleteReservationResolver(uuid: uuid)
43+ func deleteReservation(isAdmin: Bool, uuid: String, completion: @escaping ((Bool) -> Void)) {
44+ var resolver: ApiResolver!
45+ if isAdmin {
46+ resolver = ADeleteReservationResolver(uuid: uuid)
47+ } else {
48+ resolver = DeleteReservationResolver(uuid: uuid)
49+ }
50+
4551 let request = ApiRequest(resolver: resolver, httpMethod: .delete)
4652
4753 apiClient.load(request: request.request) { result in
@@ -60,14 +66,20 @@ class ReservationsService {
6066 numberOfGuests: Int,
6167 endTime: Date,
6268 uuid: String,
69+ isAdmin: Bool,
6370 completion: @escaping ((Bool) -> Void)) {
6471
6572 let reservation = ChangeReservationResolver.Request(startTime: startTime,
6673 uuid: uuid,
6774 numberOfGuests: numberOfGuests,
6875 endTime: endTime)
76+ var resolver: ApiResolver!
77+ if isAdmin {
78+ resolver = AChangeReservationResolver(data: reservation)
79+ } else {
80+ resolver = ChangeReservationResolver(data: reservation)
81+ }
6982
70− let resolver = ChangeReservationResolver(data: reservation)
7183 let request = ApiRequest(resolver: resolver, httpMethod: .put)
7284
7385 apiClient.load(request: request.request) { result in
@@ -169,4 +181,51 @@ class ReservationsService {
169181 }
170182 }
171183 }
184+
185+ func getReservationsAround(uuid: String, completion: @escaping((Result<[ReservationPeriod], GeneralError>) -> Void)) {
186+ let resolver = ReservationsAroundResolver<ReservationsAroundResponse>(reservationId: uuid)
187+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
188+
189+ apiClient.load(request: request.request) { (res) in
190+ switch res {
191+ case .failure(let err):
192+ completion(.failure(err))
193+ return
194+ case .success(let response):
195+ guard
196+ let d = response.data,
197+ let decodedData = resolver.targetClass().fromJSONToSelf(data: d)
198+ else {
199+ completion(.failure(GeneralError.decodeError))
200+ return
201+ }
202+
203+ completion(.success(decodedData.reservations))
204+ return
205+ }
206+ }
207+ }
208+
209+ func createReservation(reservation: AdminCreateReservation, completion: @escaping ((Result<String, GeneralError>) -> Void)) {
210+ let resolver = ACreateReservationResolver<ACreateAdminResonce>(data: reservation)
211+ let request = ApiRequest(resolver: resolver, httpMethod: .post)
212+
213+ apiClient.load(request: request.request) { (res) in
214+ switch res {
215+ case .failure(let err):
216+ completion(.failure(err))
217+ return
218+ case .success(let responce):
219+ guard
220+ let d = responce.data,
221+ let decData = resolver.targetClass().fromJSONToSelf(data: d)
222+ else {
223+ completion(.failure(GeneralError.decodeError))
224+ return
225+ }
226+
227+ completion(.success(decData.reservationUUID))
228+ }
229+ }
230+ }
172231}
Hookah Tau/API/Reservations api/Resolvers/Admin/AChangeReservationResolver.swiftnew file+34
@@ -0,0 +1,34 @@
1+//
2+// AChangeReservationResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 20/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+
12+/// `PUT /admin/reservations`
13+
14+class AChangeReservationResolver: ApiResolver {
15+
16+ var name: String {
17+ return "/reservations"
18+ }
19+
20+ var groupName: String? {
21+ return "/admin"
22+ }
23+
24+ var data: ChangeReservationResolver.Request
25+
26+ init(data: ChangeReservationResolver.Request) {
27+ self.data = data
28+ }
29+
30+ func parameters() -> MyCodable? {
31+ return data
32+ }
33+}
34+
Hookah Tau/API/Reservations api/Resolvers/Admin/ACreateReservationResolver.swiftnew file+37
@@ -0,0 +1,37 @@
1+//
2+// ACreateReservationResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 14/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct ACreateAdminResonce: MyCodable {
12+ var reservationUUID: String
13+}
14+
15+class ACreateReservationResolver<Responce: MyCodable>: ApiResolver {
16+ var name: String {
17+ return "/reservations"
18+ }
19+
20+ var groupName: String? {
21+ return "/admin"
22+ }
23+
24+ var data: AdminCreateReservation
25+
26+ init(data: AdminCreateReservation) {
27+ self.data = data
28+ }
29+
30+ func parameters() -> MyCodable? {
31+ return data
32+ }
33+
34+ func targetClass() -> Responce.Type {
35+ return Responce.self
36+ }
37+}
Hookah Tau/API/Reservations api/Resolvers/Admin/ADeleteReservationResolver.swiftnew file+34
@@ -0,0 +1,34 @@
1+//
2+// ADeleteReservationResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 07/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+class ADeleteReservationResolver: ApiResolver {
12+
13+ struct Request: MyCodable {
14+ var reservation: String
15+ }
16+
17+ var name: String {
18+ return "/reservations"
19+ }
20+
21+ var groupName: String? {
22+ return "/admin"
23+ }
24+
25+ var uuid: String
26+
27+ init(uuid: String) {
28+ self.uuid = uuid
29+ }
30+
31+ func parameters() -> MyCodable? {
32+ return Request(reservation: uuid)
33+ }
34+}
Hookah Tau/API/Reservations api/Resolvers/ReservationsAroundResolver.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// ReservationsAroundResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 20/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct ReservationsAroundResponse: MyCodable {
12+ var reservations: [ReservationPeriod]
13+}
14+
15+/// ` GET /reservations/around/{reservation id}`
16+class ReservationsAroundResolver<Response: MyCodable>: ApiResolver {
17+
18+ var groupName: String? {
19+ return "/reservations"
20+ }
21+
22+ var name: String {
23+ return "/around/\(reservationId)"
24+ }
25+
26+ var reservationId: String
27+
28+ init(reservationId: String) {
29+ self.reservationId = reservationId
30+ }
31+
32+ func targetClass() -> Response.Type {
33+ return Response.self
34+ }
35+}
Hookah Tau/API/ServerError.swift+1
@@ -16,6 +16,7 @@ enum GeneralError: Error {
1616 case serverError(SE)
1717 case decodeError
1818 case noData
19+ case notFound
1920 case Error400
2021 case somethingWentCompletelyWrong
2122 case alamofireError
Hookah Tau/API/Settings/SettingsService.swiftdeleted−34
@@ -1,34 +0,0 @@
1−//
2−// AdminServise.swift
3−// Hookah Tau
4−//
5−// Created by cstore on 20/12/2019.
6−// Copyright © 2019 Daria Rednikina. All rights reserved.
7−//
8−
9−import Foundation
10−
11−class SettingsService {
12−
13− private var apiClient: APIClient
14−
15− init(apiClient: APIClient) {
16− self.apiClient = apiClient
17− }
18−
19− func addOtherAdmin(phone: String, completion: @escaping ((Bool) -> Void)) {
20− let resolver = AddOtherAdminResolver(phone: phone)
21− let request = ApiRequest(resolver: resolver, httpMethod: .put)
22−
23− apiClient.load(request: request.request) { result in
24− switch result {
25− case .failure:
26− completion(false)
27− return
28− case .success:
29− completion(true)
30− return
31− }
32− }
33− }
34−}
Hookah Tau/Components/PhoneTextField.swiftnew file+63
@@ -0,0 +1,63 @@
1+//
2+// PhoneTextField.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 13/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class PhoneTextField: UITextField, UITextFieldDelegate {
12+
13+ override func awakeFromNib() {
14+ super.awakeFromNib()
15+
16+ self.delegate = self
17+ self.keyboardType = .numberPad
18+ }
19+
20+ func bind(withModel model: String) {
21+ self.text = model.formattedNumber()
22+ }
23+
24+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
25+ let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
26+ textField.text = formattedNumber(newString)
27+ return false
28+ }
29+
30+ private func formattedNumber(_ number: String) -> String {
31+ let cleanPhoneNumber = number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
32+
33+ var result = ""
34+ let phoneMask = "+X (XXX) XXX XX XX"
35+ var index = cleanPhoneNumber.startIndex
36+ for ch in phoneMask where index < cleanPhoneNumber.endIndex {
37+ if ch == "X" {
38+ result.append(cleanPhoneNumber[index])
39+ index = cleanPhoneNumber.index(after: index)
40+ } else {
41+ result.append(ch)
42+ }
43+ }
44+ return result
45+ }
46+
47+ /// +o (oxx) xxx xx xx
48+ ///
49+ /// need tor return only `x`, length is 9. And it has to have prefix +7 (9...) or +8 (9..)
50+ func getFormattedNumber() -> String? {
51+ guard let number = self.text else { return nil }
52+
53+ let cleanPhoneNumber = String(number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
54+ let hasPrefix = cleanPhoneNumber.hasPrefix("79") || cleanPhoneNumber.hasPrefix("89")
55+
56+ let result = String(cleanPhoneNumber.dropFirst(2))
57+ if result.count == 9 && hasPrefix {
58+ return result
59+ }
60+
61+ return nil
62+ }
63+}
Hookah Tau/Controllers/Admin/Clients/Cells/ClientTableViewCell.swift+12−3
@@ -20,6 +20,13 @@ class ClientTableViewCell: UITableViewCell {
2020
2121 @IBOutlet weak var numberOfReservationsLabel: UILabel!
2222
23+
24+ @IBOutlet weak var blockListLabel: UILabel!
25+
26+ @IBOutlet weak var isAdminLabel: UILabel!
27+
28+
29+
2330 // MARK: - Lifecycle
2431
2532 override func awakeFromNib() {
@@ -35,10 +42,12 @@ class ClientTableViewCell: UITableViewCell {
3542
3643 // MARK: - Bind
3744
38− func bind(withModel model: FullUser) {
39− let name = model.name ?? "❔"
40− nameLabel.text = model.isAdmin ? "👑 " + name : name
45+ func bind(withModel model: Client) {
46+ blockListLabel.isHidden = !model.isBlocked
47+ isAdminLabel.isHidden = !model.isAdmin
48+ nameLabel.text = model.name ?? "❔"
4149 phoneNumberLabel.text = model.phoneNumber.formattedNumber()
50+ numberOfReservationsLabel.text = model.reservationCount > 99 ? "99+" : "\(model.reservationCount)"
4251 }
4352
4453}
Hookah Tau/Controllers/Admin/Clients/ClientList.storyboard+110−63
@@ -1,9 +1,10 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina4_7" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
78 <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
89 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
910 </dependencies>
@@ -30,12 +31,17 @@
3031 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="vNV-bN-3No">
3132 <rect key="frame" x="0.0" y="0.0" width="179" height="53"/>
3233 <subviews>
33− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Стасик" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Cp-jh-0Gq">
34+ <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="uoO-w2-5yG">
3435 <rect key="frame" x="0.0" y="0.0" width="78.5" height="26.5"/>
35− <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
36− <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
37− <nil key="highlightedColor"/>
38− </label>
36+ <subviews>
37+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Стасик" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Cp-jh-0Gq">
38+ <rect key="frame" x="0.0" y="0.0" width="78.5" height="26.5"/>
39+ <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
40+ <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
41+ <nil key="highlightedColor"/>
42+ </label>
43+ </subviews>
44+ </stackView>
3945 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="+7 (901) 733-01-79" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0gY-h5-AV2">
4046 <rect key="frame" x="0.0" y="29" width="179" height="24"/>
4147 <fontDescription key="fontDescription" type="system" pointSize="20"/>
@@ -45,18 +51,43 @@
4551 </subviews>
4652 </stackView>
4753 <stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="tJg-ju-bFY">
48− <rect key="frame" x="310.5" y="0.0" width="32.5" height="53"/>
54+ <rect key="frame" x="219.5" y="0.0" width="123.5" height="53"/>
4955 <subviews>
50− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gzo-kW-60Z">
51− <rect key="frame" x="0.0" y="14.5" width="12.5" height="24"/>
52− <fontDescription key="fontDescription" type="system" pointSize="20"/>
53− <nil key="textColor"/>
54− <nil key="highlightedColor"/>
55− </label>
56+ <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="4et-BC-7iY">
57+ <rect key="frame" x="0.0" y="14.5" width="103.5" height="24"/>
58+ <subviews>
59+ <stackView opaque="NO" contentMode="scaleToFill" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="BHD-cD-L1c">
60+ <rect key="frame" x="0.0" y="0.0" width="51" height="24"/>
61+ <subviews>
62+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="❌" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MaQ-TW-666">
63+ <rect key="frame" x="0.0" y="0.0" width="23" height="24"/>
64+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
65+ <nil key="textColor"/>
66+ <nil key="highlightedColor"/>
67+ </label>
68+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="👑" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0Xa-FL-GVr">
69+ <rect key="frame" x="28" y="0.0" width="23" height="24"/>
70+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
71+ <nil key="textColor"/>
72+ <nil key="highlightedColor"/>
73+ </label>
74+ </subviews>
75+ </stackView>
76+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="99+" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gzo-kW-60Z">
77+ <rect key="frame" x="66" y="0.0" width="37.5" height="24"/>
78+ <fontDescription key="fontDescription" type="system" pointSize="20"/>
79+ <nil key="textColor"/>
80+ <nil key="highlightedColor"/>
81+ </label>
82+ </subviews>
83+ </stackView>
5684 <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="arrow" translatesAutoresizingMaskIntoConstraints="NO" id="DrQ-Ss-gIT">
57− <rect key="frame" x="20.5" y="16.5" width="12" height="20"/>
85+ <rect key="frame" x="111.5" y="16.5" width="12" height="20"/>
5886 </imageView>
5987 </subviews>
88+ <constraints>
89+ <constraint firstAttribute="width" constant="123.5" id="eeb-nt-pTB"/>
90+ </constraints>
6091 </stackView>
6192 </subviews>
6293 </stackView>
@@ -69,6 +100,8 @@
69100 </constraints>
70101 </tableViewCellContentView>
71102 <connections>
103+ <outlet property="blockListLabel" destination="MaQ-TW-666" id="Us0-tw-9tj"/>
104+ <outlet property="isAdminLabel" destination="0Xa-FL-GVr" id="fzz-2t-Lda"/>
72105 <outlet property="nameLabel" destination="4Cp-jh-0Gq" id="g6e-c0-x0u"/>
73106 <outlet property="numberOfReservationsLabel" destination="Gzo-kW-60Z" id="9J2-S4-fKq"/>
74107 <outlet property="phoneNumberLabel" destination="0gY-h5-AV2" id="rdb-GS-ceF"/>
@@ -86,61 +119,75 @@
86119 <point key="canvasLocation" x="0.80000000000000004" y="-112.89355322338831"/>
87120 </scene>
88121 <!--Profile Table View Controller-->
89− <scene sceneID="8Qr-sA-YQr">
122+ <scene sceneID="APa-VZ-6hd">
90123 <objects>
91− <tableViewController storyboardIdentifier="ProfileTableViewController" id="mqB-EY-UGh" customClass="ProfileTableViewController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
92− <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="N1B-QX-4Kn">
124+ <viewController storyboardIdentifier="ProfileTableViewController" id="ZVH-jf-rtc" customClass="ProfileTableViewController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
125+ <view key="view" contentMode="scaleToFill" id="rcq-M9-CvR">
93126 <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
94127 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
128+ <subviews>
129+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ciO-ia-Oob">
130+ <rect key="frame" x="0.0" y="0.0" width="375" height="65"/>
131+ <subviews>
132+ <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="IBA-bj-CWK">
133+ <rect key="frame" x="8" y="16" width="359" height="37"/>
134+ <subviews>
135+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4WC-gi-jbD" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
136+ <rect key="frame" x="0.0" y="0.0" width="180" height="37"/>
137+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
138+ <constraints>
139+ <constraint firstAttribute="width" constant="180" id="bJA-V9-gw9"/>
140+ <constraint firstAttribute="height" constant="37" id="pdo-AO-o8a"/>
141+ </constraints>
142+ <state key="normal" title="Button"/>
143+ </button>
144+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gfq-ql-lka" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
145+ <rect key="frame" x="224" y="0.0" width="135" height="37"/>
146+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
147+ <constraints>
148+ <constraint firstAttribute="width" constant="135" id="Xn3-v6-SLp"/>
149+ <constraint firstAttribute="height" constant="37" id="z68-vW-NjK"/>
150+ </constraints>
151+ <state key="normal" title="Button"/>
152+ </button>
153+ </subviews>
154+ </stackView>
155+ </subviews>
156+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
157+ <constraints>
158+ <constraint firstItem="IBA-bj-CWK" firstAttribute="leading" secondItem="ciO-ia-Oob" secondAttribute="leading" constant="8" id="WZS-Vq-tuP"/>
159+ <constraint firstAttribute="trailing" secondItem="IBA-bj-CWK" secondAttribute="trailing" constant="8" id="Yk3-D1-1kg"/>
160+ <constraint firstItem="IBA-bj-CWK" firstAttribute="top" secondItem="ciO-ia-Oob" secondAttribute="top" constant="16" id="fWO-oj-NuN"/>
161+ <constraint firstItem="IBA-bj-CWK" firstAttribute="leading" secondItem="ciO-ia-Oob" secondAttribute="leading" constant="8" id="gP2-jh-5El"/>
162+ <constraint firstAttribute="height" constant="65" id="pzZ-zw-kbc"/>
163+ </constraints>
164+ </view>
165+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="T0s-lX-Z5m">
166+ <rect key="frame" x="0.0" y="65" width="375" height="602"/>
167+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
168+ </view>
169+ </subviews>
95170 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
96− <view key="tableHeaderView" contentMode="scaleToFill" id="oN2-r5-lTy">
97− <rect key="frame" x="0.0" y="0.0" width="375" height="65"/>
98− <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
99− <subviews>
100− <stackView opaque="NO" contentMode="scaleToFill" spacing="47" translatesAutoresizingMaskIntoConstraints="NO" id="UpQ-Uf-0Kc">
101− <rect key="frame" x="8" y="16" width="359" height="37"/>
102− <subviews>
103− <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nFA-T3-Dxe" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
104− <rect key="frame" x="0.0" y="0.0" width="177" height="37"/>
105− <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
106− <constraints>
107− <constraint firstAttribute="height" constant="37" id="41i-38-mHD"/>
108− <constraint firstAttribute="width" constant="174" id="po9-OR-zq1"/>
109− </constraints>
110− <state key="normal" title="Button"/>
111− </button>
112− <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HkY-u9-thv" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
113− <rect key="frame" x="224" y="0.0" width="135" height="37"/>
114− <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
115− <constraints>
116− <constraint firstAttribute="height" constant="37" id="LDw-fn-Xem"/>
117− <constraint firstAttribute="width" constant="135" id="swI-oV-Khe"/>
118− </constraints>
119− <state key="normal" title="Button"/>
120− </button>
121− </subviews>
122− </stackView>
123− </subviews>
124− <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
125− <constraints>
126− <constraint firstItem="UpQ-Uf-0Kc" firstAttribute="top" secondItem="oN2-r5-lTy" secondAttribute="top" constant="16" id="58o-fg-n3i"/>
127− <constraint firstItem="UpQ-Uf-0Kc" firstAttribute="leading" secondItem="oN2-r5-lTy" secondAttribute="leading" constant="8" id="5ik-TQ-N4U"/>
128− <constraint firstAttribute="trailing" secondItem="UpQ-Uf-0Kc" secondAttribute="trailing" constant="8" id="8Ca-3C-Xld"/>
129− </constraints>
130− </view>
131− <connections>
132− <outlet property="dataSource" destination="mqB-EY-UGh" id="YZe-zB-7f3"/>
133− <outlet property="delegate" destination="mqB-EY-UGh" id="JPi-98-LhH"/>
134− </connections>
135− </tableView>
171+ <constraints>
172+ <constraint firstItem="ciO-ia-Oob" firstAttribute="trailing" secondItem="tIS-3m-xwG" secondAttribute="trailing" id="3An-sM-OCN"/>
173+ <constraint firstItem="ciO-ia-Oob" firstAttribute="top" secondItem="tIS-3m-xwG" secondAttribute="top" id="5zl-uZ-sTi"/>
174+ <constraint firstItem="T0s-lX-Z5m" firstAttribute="leading" secondItem="tIS-3m-xwG" secondAttribute="leading" id="7qA-Hh-3Us"/>
175+ <constraint firstItem="T0s-lX-Z5m" firstAttribute="trailing" secondItem="tIS-3m-xwG" secondAttribute="trailing" id="9VD-9Q-wGX"/>
176+ <constraint firstItem="T0s-lX-Z5m" firstAttribute="top" secondItem="ciO-ia-Oob" secondAttribute="bottom" id="RSL-fo-wIb"/>
177+ <constraint firstItem="ciO-ia-Oob" firstAttribute="leading" secondItem="tIS-3m-xwG" secondAttribute="leading" id="Tuq-su-gbn"/>
178+ <constraint firstItem="T0s-lX-Z5m" firstAttribute="bottom" secondItem="tIS-3m-xwG" secondAttribute="bottom" id="iQj-LM-Im9"/>
179+ </constraints>
180+ <viewLayoutGuide key="safeArea" id="tIS-3m-xwG"/>
181+ </view>
136182 <connections>
137− <outlet property="copyButton" destination="HkY-u9-thv" id="RLs-cg-y3g"/>
138− <outlet property="phoneButton" destination="nFA-T3-Dxe" id="d7H-jf-NB5"/>
183+ <outlet property="contentView" destination="T0s-lX-Z5m" id="9Z0-5T-f7Z"/>
184+ <outlet property="copyButton" destination="gfq-ql-lka" id="BNi-Ip-AyF"/>
185+ <outlet property="phoneButton" destination="4WC-gi-jbD" id="xlf-g7-CzU"/>
139186 </connections>
140− </tableViewController>
141− <placeholder placeholderIdentifier="IBFirstResponder" id="1YW-I1-Y6m" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
187+ </viewController>
188+ <placeholder placeholderIdentifier="IBFirstResponder" id="g4Y-1k-Lxr" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
142189 </objects>
143− <point key="canvasLocation" x="880.79999999999995" y="-111.0944527736132"/>
190+ <point key="canvasLocation" x="1188" y="-113"/>
144191 </scene>
145192 </scenes>
146193 <resources>
Hookah Tau/Controllers/Admin/Clients/ClientsCoordinator.swift+1−1
@@ -21,7 +21,7 @@ class ClientsCoordinator: BaseCoordinator {
2121 }
2222
2323 /// TODO
24− func openUserProfile(_ profile: FullUser) {
24+ func openUserProfile(_ profile: Client) {
2525 let profileCoordinator = ClientProfileCoordinator(navigationController: navigationController)
2626 profileCoordinator.user = profile
2727
Hookah Tau/Controllers/Admin/Clients/ClientsViewController.swift+175−8
@@ -8,34 +8,70 @@
88
99import UIKit
1010
11−class ClientsViewController: UITableViewController {
11+class ClientsViewController: BaseTableViewController {
1212
1313 // MARK: - Properties
1414
15− var dataSource: [FullUser] = [] {
15+ var dataSource: [Client] = [] {
1616 didSet {
17− print(dataSource)
1817 tableView.reloadData()
1918 }
2019 }
2120
21+ var filteredDataSource: [Client] = []
22+
2223 var clientsService: ClientsService?
2324
2425 weak var coordinator: ClientsCoordinator?
2526
27+ var searchControl: UISearchController?
28+
29+ var isFiltering: Bool {
30+ return searchControl?.isActive ?? false && !searchBarIsEmpty()
31+ }
32+
33+ func searchBarIsEmpty() -> Bool {
34+ return searchControl?.searchBar.text?.isEmpty ?? true
35+ }
36+
2637 // MARK: - Lifecycle
2738
2839 override func viewDidLoad() {
2940 super.viewDidLoad()
3041
31− navigationItem.title = "Клиенты"
42+ setupNavigationBar()
43+ setupSearchController()
3244 clientsService = ClientsService(apiClient: APIClient.shared)
3345 }
3446
47+ override func viewWillAppear(_ animated: Bool) {
48+ super.viewWillAppear(animated)
49+ tabBarController?.tabBar.isHidden = false
50+ }
51+
3552 override func viewDidAppear(_ animated: Bool) {
3653 super.viewDidAppear(animated)
3754 loadClients()
3855 }
56+
57+ // MARK: - Setup
58+
59+ func setupNavigationBar() {
60+
61+ navigationItem.title = "Клиенты"
62+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareCSVFile))
63+ }
64+
65+ func setupSearchController() {
66+ searchControl = UISearchController(searchResultsController: nil)
67+ navigationItem.searchController = searchControl
68+ navigationItem.hidesSearchBarWhenScrolling = false
69+ searchControl?.obscuresBackgroundDuringPresentation = false
70+ definesPresentationContext = true
71+ searchControl?.searchResultsUpdater = self
72+ }
73+
74+ // MARK: - Api calls
3975
4076 /// Загружаем клиентов кальянки
4177 func loadClients() {
@@ -49,24 +85,155 @@ class ClientsViewController: UITableViewController {
4985 })
5086 }
5187
88+ // MARK: - Button handlers
89+
90+ /// https://developer.apple.com/design/human-interface-guidelines/ios/views/activity-views/
91+ @objc func shareCSVFile() {
92+ //Your CSV text
93+ let str = HFunc.main.exportClientsToCSV(dataSource)
94+ let filename = getDocumentsDirectory().appendingPathComponent("clients.csv")
95+
96+ do {
97+ try str.write(toFile: filename, atomically: true, encoding: String.Encoding.utf8)
98+
99+ let fileData = NSURL(fileURLWithPath: filename)
100+
101+ let objectsToShare = [fileData]
102+ let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
103+
104+ self.present(activityVC, animated: true, completion: nil)
105+
106+ } catch {
107+ self.displayAlert(with: "По какой-то причине не могу сохранить файл!")
108+ }
109+ }
110+
111+ func getDocumentsDirectory() -> NSString {
112+ let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
113+ let documentsDirectory = paths[0]
114+ return documentsDirectory as NSString
115+ }
116+
117+ // MARK: - Change blocklist status
118+
119+ func updateUserBlockStatus(wasBlocked: Bool, phone: String, indexPath: IndexPath) {
120+ let crudMethod = wasBlocked ? CrudMethod.delete : CrudMethod.put
121+ clientsService?.changeBlockList(whatToDo: crudMethod, phone: phone, completion: { (res) in
122+ if res {
123+ self.tableView?.beginUpdates()
124+ self.tableView?.reloadRows(at: [indexPath], with: .automatic)
125+ if self.isFiltering {
126+ self.filteredDataSource[indexPath.row].isBlocked = !wasBlocked
127+ let changed = self.filteredDataSource[indexPath.row]
128+ let filtered = self.dataSource.map { (one: Client) -> Client in
129+ if one.uuid == changed.uuid {
130+ return changed
131+ } else { return one } }
132+ self.dataSource = filtered
133+ } else {
134+ self.dataSource[indexPath.row].isBlocked = !wasBlocked
135+ }
136+ self.tableView?.endUpdates()
137+ } else {
138+ self.displayAlert(with: "Не удалось \(wasBlocked ? "удалить" : "добавить") пользователя \(wasBlocked ? "из" : "в") чс! Попробуйте еще раз!")
139+ return
140+ }
141+ })
142+ }
52143
53− // MARK: - Tableview
144+ func updateAdminStatus(user: Client, indexPath: IndexPath) {
145+
146+ let wasAdmin = user.isAdmin
147+ let crudMethod = wasAdmin ? CrudMethod.delete : CrudMethod.put
148+ let data = user.isAdmin ? user.uuid : user.phoneNumber
149+
150+ clientsService?.changeAdmin(crud: crudMethod, data: data, completion: { (res) in
151+ if res {
152+ self.tableView?.beginUpdates()
153+ self.tableView?.reloadRows(at: [indexPath], with: .automatic)
154+ if self.isFiltering {
155+ self.filteredDataSource[indexPath.row].isAdmin = !wasAdmin
156+ let changed = self.filteredDataSource[indexPath.row]
157+ let filtered = self.dataSource.map { (one: Client) -> Client in
158+ if one.uuid == changed.uuid {
159+ return changed
160+ } else { return one } }
161+ self.dataSource = filtered
162+ } else {
163+ self.dataSource[indexPath.row].isAdmin = !wasAdmin
164+ }
165+ self.tableView?.endUpdates()
166+ } else {
167+ self.displayAlert(with: "Не удалось \(wasAdmin ? "распромоутить" : "запромоутить") пользователя! Попробуйте еще раз!")
168+ return
169+ }
170+ })
171+ }
172+}
173+
174+// MARK: - Tableview
175+
176+extension ClientsViewController {
54177
55178 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
56179 let cell = tableView.dequeueReusableCell(withIdentifier: clientTableViewCellId, for: indexPath) as! ClientTableViewCell
57180
58− let userModel = dataSource[indexPath.row]
181+ let userModel = isFiltering ? filteredDataSource[indexPath.row] : dataSource[indexPath.row]
59182 cell.bind(withModel: userModel)
60183
61184 return cell
62185 }
63186
64187 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
65− return dataSource.count
188+ return isFiltering ? filteredDataSource.count : dataSource.count
66189 }
67190
68191 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
69− let user = dataSource[indexPath.row]
192+ let user = isFiltering ? filteredDataSource[indexPath.row] : dataSource[indexPath.row]
70193 coordinator?.openUserProfile(user)
194+ searchControl?.dismiss(animated: true, completion: nil)
195+ }
196+
197+ // Update
198+
199+ override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
200+ return true
201+ }
202+
203+ override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
204+
205+ let user = isFiltering ? filteredDataSource[indexPath.row] : dataSource[indexPath.row]
206+ let blockButton = UITableViewRowAction(style: .normal, title: "\(!user.isBlocked ? "в чс" : "убрать из чс")") { _,_ in
207+
208+ self.updateUserBlockStatus(wasBlocked: user.isBlocked, phone: user.phoneNumber, indexPath: indexPath)
209+ }
210+
211+ blockButton.backgroundColor = .black
212+
213+ return [blockButton]
214+ }
215+
216+ override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
217+
218+ let user = isFiltering ? filteredDataSource[indexPath.row] : dataSource[indexPath.row]
219+
220+ let promote = UIContextualAction(style: .normal, title: "\(!user.isAdmin ? "дать админку" : "забрать админку")") { (_, _, _) in
221+ self.updateAdminStatus(user: user, indexPath: indexPath)
222+ }
223+ promote.backgroundColor = .black
224+
225+ return UISwipeActionsConfiguration(actions: [promote])
226+ }
227+}
228+
229+extension ClientsViewController: UISearchResultsUpdating {
230+ func filterContentForSearchText(_ searchText: String, scope: String = "All")
231+ {
232+ filteredDataSource = dataSource.filter { ($0.name?.lowercased().contains(searchText.lowercased()) ?? false) || ($0.phoneNumber.lowercased().contains(searchText.lowercased())) }
233+ tableView.reloadData()
234+ }
235+
236+ func updateSearchResults(for searchController: UISearchController) {
237+ filterContentForSearchText(searchController.searchBar.text ?? "")
71238 }
72239}
Hookah Tau/Controllers/Admin/Clients/Profile/ClientProfileCoordinator.swift+25−2
@@ -11,19 +11,42 @@ import UIKit
1111
1212class ClientProfileCoordinator: BaseCoordinator {
1313
14− var user: FullUser?
14+ var user: Client?
1515
1616 override func start() {
1717 let storyboard = UIStoryboard(name: "ClientList", bundle: nil)
1818 let viewController = storyboard.instantiateViewController(withIdentifier: "ProfileTableViewController") as? ProfileTableViewController
1919 viewController?.user = user
20+ viewController?.coodinator = self
2021 guard let vc = viewController else { return }
2122 navigationController?.pushViewController(vc, animated: true)
2223 }
2324
2425
2526 // Создание бронирования из профиля клиента
26− func createReservation(forUserWithPhone phoneNumber: String) {
27+ func createReservation(forUserWithPhone phoneNumber: String, user fullUser: FullUser) {
28+ let coordinator = AdminFirstStepReservationViewCoordinator(navigationController: navigationController)
29+ coordinator.phone = phoneNumber
30+ coordinator.fullUser = fullUser
2731
32+ addDependency(coordinator)
33+ coordinator.didFinish = { [weak self] in
34+ self?.removeDependency(coordinator)
35+ self?.navigationController?.popViewController(animated: true)
36+ }
37+
38+ coordinator.start()
39+ }
40+
41+ // Просмотр (и редактирование) брони
42+ func seeExistingReservation(data: ReservationWithUser) {
43+ let reservationCoordinator = AdminReservationEditingCoordinator(navigationController: navigationController)
44+ addDependency(reservationCoordinator)
45+ reservationCoordinator.reservationData = data
46+ reservationCoordinator.didEndFlow = { [weak self] in
47+ self?.removeDependency(reservationCoordinator)
48+ self?.navigationController?.popViewController(animated: true)
49+ }
50+ reservationCoordinator.start()
2851 }
2952}
Hookah Tau/Controllers/Admin/Clients/Profile/ProfileTableViewController.swift+181−22
@@ -8,7 +8,7 @@
88
99import UIKit
1010
11−class ProfileTableViewController: UITableViewController {
11+class ProfileTableViewController: BaseViewController {
1212
1313 // MARK: - IBAOutlets
1414
@@ -25,26 +25,46 @@ class ProfileTableViewController: UITableViewController {
2525 @IBOutlet weak var copyButton: Button! {
2626 didSet {
2727 let style = BlackButtonStyle()
28− style.apply(to: copyButton, withTitle: "скопировать")
28+ style.apply(to: copyButton, withTitle: "СКОПИРОВАТЬ")
29+ copyButton.addTarget(self, action: #selector(copyNumber(_:)), for: .touchUpInside)
2930 }
3031 }
3132
33+
34+ @IBOutlet weak var contentView: UIView!
35+
3236 // MARK: - properties
3337
38+ var tableView: UITableView?
39+
3440 var clientsService: ClientsService?
3541
36− var user: FullUser? {
37− didSet {
38− guard let u = user else { return }
39− navigationItem.title = u.name
40− }
41− }
42+ var reservationsService: ReservationsService?
43+
44+ var user: Client?
4245
4346 weak var coodinator: ClientProfileCoordinator?
4447
48+ var noReserationsYetView: AdminNoReservationsView?
49+
50+ var activityIndicator: UIActivityIndicatorView?
51+
52+ var refreshControl: UIRefreshControl?
53+
4554 var dataSource: [Reservation] = [] {
4655 didSet {
47− tableView.reloadData()
56+ guard let empty = noReserationsYetView else { return }
57+ if dataSource.isEmpty {
58+ empty.alpha = 1
59+ tableView?.alpha = 0
60+ } else {
61+ empty.alpha = 0
62+ tableView?.alpha = 1
63+ }
64+
65+ refreshControl?.endRefreshing()
66+ activityIndicator?.stopAnimating()
67+ tableView?.reloadData()
4868 }
4969 }
5070
@@ -53,32 +73,143 @@ class ProfileTableViewController: UITableViewController {
5373 override func viewDidLoad() {
5474 super.viewDidLoad()
5575
56− tableView?.register(ReservationCell.self, forCellReuseIdentifier: reservationCell)
57−
5876 clientsService = ClientsService(apiClient: APIClient.shared)
77+ reservationsService = ReservationsService(apiClient: APIClient.shared)
78+
79+ setupNavBar()
80+ setupTable()
81+ setupActivityIndicator()
82+ //loadReservations()
83+ }
84+
85+ override func viewWillAppear(_ animated: Bool) {
86+ super.viewWillAppear(animated)
87+ tabBarController?.tabBar.isHidden = true
88+ }
89+
90+ override func viewDidAppear(_ animated: Bool) {
91+ super.viewDidAppear(animated)
5992 loadReservations()
6093 }
6194
95+ // MARK: - Setup
96+
97+ func setupNavBar() {
98+ guard let u = user else { return }
99+
100+ navigationItem.title = "\(u.name ?? "❔")\(u.isAdmin ? "👑" : "")\(u.isBlocked ? "❌" : "")"
101+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(book))
102+ }
103+
104+ func setupTable() {
105+ tableView = UITableView()
106+
107+ // refresh control
108+ refreshControl = UIRefreshControl()
109+ refreshControl?.addTarget(self, action: #selector(refresh), for: .valueChanged)
110+ tableView?.refreshControl = refreshControl
111+
112+ tableView?.dataSource = self
113+ tableView?.delegate = self
114+
115+ noReserationsYetView = AdminNoReservationsView.loadFromNib()
116+ noReserationsYetView?.setup(withTitle: "У этого пользователя пока нет ни одной брони :(")
117+ noReserationsYetView?.bookButton?.addTarget(self, action: #selector(book), for: .touchUpInside)
118+ contentView.addSubviewThatFills(noReserationsYetView)
119+ noReserationsYetView?.alpha = 0
120+
121+ tableView?.register(ReservationCell.self, forCellReuseIdentifier: reservationCell)
122+
123+ contentView.addSubviewThatFills(tableView)
124+
125+ tableView?.alpha = 0
126+ }
127+
128+ func setupActivityIndicator() {
129+
130+ activityIndicator = UIActivityIndicatorView()
131+ view.addSubviewThatFills(activityIndicator)
132+ activityIndicator?.startAnimating()
133+
134+ }
135+
136+ // MARK: - Api calls
137+
62138 func loadReservations() {
63139 guard let uuid = user?.uuid else { return }
64140 clientsService?.loadReservationsForUser(withUUID: uuid,
65141 completion: { [weak self] (res) in
66142 switch res {
67143 case .failure(let err):
68− print(err)
144+ self?.displayAlert(forError: err)
69145 case .success(let data):
70146 self?.dataSource = data
71147 }
72148 })
73149 }
74150
75− // MARK: - Table view data source
151+
152+ func deleteReservation(uuid: String, completion: @escaping (([Reservation]?) -> Void)) {
153+ reservationsService?.deleteReservation(isAdmin: true, uuid: uuid) { result in
154+ if result {
155+ let newReservations = self.dataSource.filter { $0.uuid != uuid }
156+ completion(newReservations)
157+ return
158+ }
159+
160+ completion(nil)
161+ }
162+ }
163+
164+ // MARK: - Button handlers
165+
166+ @objc func callUser() {
167+ guard
168+ let u = user,
169+ let urlPhone = URL(string: "tel://79\(u.phoneNumber)")
170+ else { return }
171+
172+ UIApplication.shared.open(urlPhone)
173+ }
174+
175+ @objc func copyNumber(_ sender: UIButton) {
176+ guard
177+ let u = user
178+ else { return }
179+ //Copy a string to the pasteboard.
180+ UIPasteboard.general.string = "79\(u.phoneNumber)"
181+
182+ //Alert
183+ self.displayAlert(with: "Номер скопирован!")
184+ }
185+
186+ @objc func refresh() {
187+ loadReservations()
188+ }
189+
190+ @objc func book() {
191+ guard
192+ let phone = user?.phoneNumber,
193+ let user = user
194+ else { return }
195+
196+ let fullUser = FullUser(uuid: user.uuid,
197+ name: user.name,
198+ phoneNumber: user.phoneNumber,
199+ isAdmin: user.isAdmin)
200+ coodinator?.createReservation(forUserWithPhone: phone, user: fullUser)
201+ }
202+}
203+
204+// MARK: - Table view data source
205+
206+extension ProfileTableViewController: UITableViewDelegate, UITableViewDataSource {
76207
77− override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
208+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
78209 return dataSource.count
79210 }
80211
81− override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
212+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
82213 let cell = tableView.dequeueReusableCell(withIdentifier: reservationCell, for: indexPath) as! ReservationCell
83214
84215 cell.bind(withModel: dataSource[indexPath.row])
@@ -86,13 +217,41 @@ class ProfileTableViewController: UITableViewController {
86217 return cell
87218 }
88219
89− @objc func callUser() {
90− guard
91− let u = user,
92− let urlPhone = URL(string: "tel://79\(u.phoneNumber)")
93− else { return }
220+ // tap
221+
222+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
223+ guard let u = user else { return }
224+ let r = dataSource[indexPath.row]
225+ let user = FullUser(uuid: u.uuid, name: u.name, phoneNumber: u.phoneNumber, isAdmin: u.isAdmin)
226+ let reservation = ReservationWithUser(reservation: r, user: user)
227+ coodinator?.seeExistingReservation(data: reservation)
228+ }
229+
230+ // Update
231+
232+ func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
233+ return true
234+ }
235+
236+ func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
94237
95− UIApplication.shared.open(urlPhone)
238+ let uuid = dataSource[indexPath.row].uuid
239+ let cancelButton = UITableViewRowAction(style: .normal, title: "отменить бронь") { _,_ in
240+
241+ self.deleteReservation(uuid: uuid) { optionalNewVal in
242+ if let newval = optionalNewVal {
243+ self.tableView?.beginUpdates()
244+ self.tableView?.deleteRows(at: [indexPath], with: .automatic)
245+ self.dataSource = newval
246+ self.tableView?.endUpdates()
247+ } else {
248+ self.displayAlert(with: "Не удалось удалить вашу бронь! Попробуйте еще раз!")
249+ }
250+ }
251+ }
252+
253+ cancelButton.backgroundColor = .black
254+
255+ return [cancelButton]
96256 }
97−
98257}
Hookah Tau/Controllers/Admin/Reservations/AdminAddressCoordinator.swift+1
@@ -21,6 +21,7 @@ class AdminAddressCoordinator: BaseCoordinator {
2121
2222 private func openReservations(forEstablishment id: Int) {
2323 let reservationsCoordinator = AdminReservationsCoordinator(navigationController: navigationController)
24+ addDependency(reservationsCoordinator)
2425 reservationsCoordinator.establishmentId = id
2526 reservationsCoordinator.start()
2627 }
Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingCoordinator.swiftnew file+31
@@ -0,0 +1,31 @@
1+//
2+// AdminReservationEditingCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 20/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+
12+class AdminReservationEditingCoordinator: BaseCoordinator, ShowReservationCoordinator {
13+
14+ // MARK: - Peoperties
15+
16+ var didFinish: (() -> Void)?
17+
18+ var reservationData: ReservationWithUser?
19+
20+ // MARK: - Lifecycle
21+
22+ override func start() {
23+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
24+ let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationEditingViewController") as! AdminReservationEditingViewController
25+ vc.reservationData = reservationData
26+ vc.coordinator = self
27+
28+ navigationController?.pushViewController(vc, animated: true)
29+ }
30+
31+}
Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingViewController.swiftnew file+187
@@ -0,0 +1,187 @@
1+//
2+// AdminReservationEditingViewController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 20/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminReservationEditingViewController: BaseViewController {
12+
13+ // MARK:- IBAOutlets
14+
15+ @IBOutlet weak var tableContentView: UIView!
16+
17+ @IBOutlet weak var mainContentView: UIView!
18+
19+ // MARK:- Properties
20+
21+ var mapView: MapImageScroll?
22+
23+ var reservationView: AdminReservationView?
24+
25+ var reservationData: ReservationWithUser?
26+
27+ weak var coordinator: ShowReservationCoordinator?
28+
29+ var reservationService: ReservationsService?
30+
31+ var reservedPeriods: [ReservationPeriod] = [] {
32+ didSet {
33+ if let _ = reservationView {
34+ setupReservationViewWithIntervals()
35+ }
36+ }
37+ }
38+
39+ // MARK:- Lifecycle
40+
41+ override func viewDidLoad() {
42+ super.viewDidLoad()
43+
44+ setupContentViews()
45+
46+ reservationService = ReservationsService(apiClient: APIClient.shared)
47+
48+ loadReservedPeriods()
49+
50+ setupButtons()
51+ }
52+
53+ override func viewWillAppear(_ animated: Bool) {
54+ super.viewWillAppear(animated)
55+
56+ tabBarController?.tabBar.isHidden = true
57+ }
58+
59+ // MARK: - Setup
60+
61+ func setupButtons() {
62+
63+ reservationView?.saveButton?.addTarget(self, action: #selector(saveReservation), for: .touchUpInside)
64+ reservationView?.cancelButton?.addTarget(self, action: #selector(cancelReservation), for: .touchUpInside)
65+ }
66+
67+ func setupContentViews() {
68+ guard
69+ let model = reservationData
70+ else { return }
71+
72+ if model.establishment == 1 {
73+ mapView = MapView1.loadFromNib()
74+ } else {
75+ mapView = MapView2.loadFromNib()
76+ }
77+
78+ navigationItem.title = TotalStorage.standard.getEstablishment(model.establishment)?.address
79+
80+ mapView?.isUserInteractionEnabled = false
81+
82+ tableContentView.addSubviewThatFills(mapView)
83+ mapView?.scrollToTable(table: model.reservedTable)
84+
85+ reservationView = AdminReservationView.loadFromNib()
86+ reservationView?.bind(withModel: model)
87+
88+ mainContentView.addSubviewThatFills(reservationView)
89+ }
90+
91+ func setupReservationViewWithIntervals() {
92+ reservationView?.bind(withIntervals: reservedPeriods)
93+ }
94+
95+ func loadReservedPeriods() {
96+ guard
97+ let model = reservationData,
98+ let uuid = model.uuid
99+ else {
100+ setupReservationViewWithIntervals()
101+ return }
102+
103+ reservationService?.getReservationsAround(uuid: uuid, completion: { [weak self] (res) in
104+ switch res {
105+ case .failure(let err):
106+ self?.displayAlert(forError: err)
107+ return
108+ case .success(let reserv):
109+ self?.reservedPeriods = reserv
110+ return
111+ }
112+ })
113+ }
114+
115+ // MARK:- Handlers
116+
117+ @objc func saveReservation() {
118+ guard
119+ let r = reservationView?.data,
120+ let uuid = reservationData?.uuid
121+ else {
122+ createReservation()
123+ return
124+ }
125+
126+ reservationView?.saveButton?.loading = true
127+
128+ reservationService?.updateReservation(startTime: r.startTime,
129+ numberOfGuests: r.numberOfGuests,
130+ endTime: r.endTime,
131+ uuid: uuid,
132+ isAdmin: true,
133+ completion: { [weak self] (res) in
134+ if res {
135+ self?.coordinator?.didFinish?()
136+ } else {
137+ self?.displayAlert(with: "Что-то пошло не так! Попробуешь еще раз?")
138+ }
139+
140+ self?.reservationView?.saveButton?.loading = false
141+ })
142+ }
143+
144+ func createReservation() {
145+ guard
146+ let r = reservationData,
147+ let d = reservationData?.owner.phoneNumber else { return }
148+
149+ let data = ReservationData(establishment: r.establishment,
150+ startTime: r.startTime,
151+ endTime: r.endTime,
152+ numberOfGuests: r.numberOfGuests,
153+ reservedTable: r.reservedTable)
154+ print(data)
155+ reservationService?.createReservation(reservation: AdminCreateReservation(reservation: data, user: d), completion: { (res) in
156+ switch res {
157+ case .failure(_):
158+ self.displayAlert(with: "Что-то пошло не так!\nПопробуешь еще раз?")
159+ return
160+ case .success(let uuid):
161+ self.reservationData?.uuid = uuid
162+ self.coordinator?.didFinish?()
163+ return
164+ }
165+ })
166+ }
167+
168+ @objc func cancelReservation() {
169+ guard
170+ let uuid = reservationData?.uuid
171+ else {
172+ cancelPrereservation()
173+ return }
174+
175+ reservationService?.deleteReservation(isAdmin: true, uuid: uuid, completion: { [weak self] (res) in
176+ if res {
177+ self?.coordinator?.didFinish?()
178+ } else {
179+ self?.displayAlert(with: "Что-то пошло не так!\nПопробуешь еще раз?")
180+ }
181+ })
182+ }
183+
184+ func cancelPrereservation() {
185+ coordinator?.didFinish?()
186+ }
187+}
Hookah Tau/Controllers/Admin/Reservations/AdminReservations.storyboard+105
@@ -23,5 +23,110 @@
2323 </objects>
2424 <point key="canvasLocation" x="111" y="159"/>
2525 </scene>
26+ <!--Admin Reservation Editing View Controller-->
27+ <scene sceneID="hWe-Nm-jBL">
28+ <objects>
29+ <viewController storyboardIdentifier="AdminReservationEditingViewController" id="BgQ-ca-cuZ" customClass="AdminReservationEditingViewController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
30+ <view key="view" contentMode="scaleToFill" id="tjV-EU-XvW">
31+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
32+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
33+ <subviews>
34+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uNU-TW-vfq">
35+ <rect key="frame" x="0.0" y="262" width="375" height="385"/>
36+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
37+ <constraints>
38+ <constraint firstAttribute="height" constant="385" id="wfZ-i9-ruU"/>
39+ </constraints>
40+ </view>
41+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ghQ-Jv-1Uo">
42+ <rect key="frame" x="0.0" y="0.0" width="375" height="254"/>
43+ <color key="backgroundColor" systemColor="systemGreenColor" red="0.20392156859999999" green="0.78039215689999997" blue="0.34901960780000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
44+ </view>
45+ </subviews>
46+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
47+ <constraints>
48+ <constraint firstItem="ghQ-Jv-1Uo" firstAttribute="leading" secondItem="qeh-vb-wIH" secondAttribute="leading" id="6ue-fN-8T9"/>
49+ <constraint firstAttribute="bottom" secondItem="uNU-TW-vfq" secondAttribute="bottom" constant="20" symbolic="YES" id="CEP-Ac-DSe"/>
50+ <constraint firstItem="uNU-TW-vfq" firstAttribute="leading" secondItem="qeh-vb-wIH" secondAttribute="leading" id="Ff2-mz-GAH"/>
51+ <constraint firstItem="ghQ-Jv-1Uo" firstAttribute="top" secondItem="qeh-vb-wIH" secondAttribute="top" id="MMC-Og-ggL"/>
52+ <constraint firstItem="uNU-TW-vfq" firstAttribute="trailing" secondItem="qeh-vb-wIH" secondAttribute="trailing" id="fRe-ZZ-rez"/>
53+ <constraint firstItem="ghQ-Jv-1Uo" firstAttribute="trailing" secondItem="qeh-vb-wIH" secondAttribute="trailing" id="hPh-8O-if8"/>
54+ <constraint firstItem="uNU-TW-vfq" firstAttribute="top" secondItem="ghQ-Jv-1Uo" secondAttribute="bottom" constant="8" symbolic="YES" id="rDO-oM-rwy"/>
55+ </constraints>
56+ <viewLayoutGuide key="safeArea" id="qeh-vb-wIH"/>
57+ </view>
58+ <connections>
59+ <outlet property="mainContentView" destination="uNU-TW-vfq" id="Fy6-pC-ach"/>
60+ <outlet property="tableContentView" destination="ghQ-Jv-1Uo" id="GN2-MW-a2J"/>
61+ </connections>
62+ </viewController>
63+ <placeholder placeholderIdentifier="IBFirstResponder" id="LNT-df-eny" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
64+ </objects>
65+ <point key="canvasLocation" x="959.20000000000005" y="158.77061469265368"/>
66+ </scene>
67+ <!--Admin Second Step Reservation View Controller-->
68+ <scene sceneID="VhI-Z9-iqB">
69+ <objects>
70+ <viewController storyboardIdentifier="AdminSecondStepReservationViewController" id="eNJ-Ja-201" customClass="AdminSecondStepReservationViewController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
71+ <view key="view" contentMode="scaleToFill" id="7mk-5I-JJs">
72+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
73+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
74+ <subviews>
75+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oAV-GW-iea">
76+ <rect key="frame" x="0.0" y="0.0" width="375" height="293"/>
77+ <color key="backgroundColor" systemColor="systemIndigoColor" red="0.34509803919999998" green="0.33725490200000002" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
78+ </view>
79+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a4v-ng-lm7">
80+ <rect key="frame" x="0.0" y="293" width="375" height="374"/>
81+ <subviews>
82+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="13" translatesAutoresizingMaskIntoConstraints="NO" id="1Bb-cO-4qW">
83+ <rect key="frame" x="20" y="22" width="335" height="88.5"/>
84+ <subviews>
85+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Номер клиента" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bME-Wr-gh7">
86+ <rect key="frame" x="62.5" y="0.0" width="210.5" height="33.5"/>
87+ <fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
88+ <nil key="textColor"/>
89+ <nil key="highlightedColor"/>
90+ </label>
91+ <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="+7 (988) 888-88-88" textAlignment="natural" minimumFontSize="28" translatesAutoresizingMaskIntoConstraints="NO" id="wBC-oa-ekQ" customClass="PhoneTextField" customModule="Hookah_Tau" customModuleProvider="target">
92+ <rect key="frame" x="13.5" y="46.5" width="308.5" height="42"/>
93+ <fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
94+ <textInputTraits key="textInputTraits"/>
95+ </textField>
96+ </subviews>
97+ </stackView>
98+ </subviews>
99+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
100+ <constraints>
101+ <constraint firstItem="1Bb-cO-4qW" firstAttribute="top" secondItem="a4v-ng-lm7" secondAttribute="top" constant="22" id="7fb-Up-Wmb"/>
102+ <constraint firstAttribute="trailing" secondItem="1Bb-cO-4qW" secondAttribute="trailing" constant="20" id="934-Ef-0xf"/>
103+ <constraint firstItem="1Bb-cO-4qW" firstAttribute="leading" secondItem="a4v-ng-lm7" secondAttribute="leading" constant="20" id="sr5-aO-q1p"/>
104+ <constraint firstAttribute="height" constant="374" id="uRW-2Q-OyM"/>
105+ </constraints>
106+ </view>
107+ </subviews>
108+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
109+ <constraints>
110+ <constraint firstItem="a4v-ng-lm7" firstAttribute="leading" secondItem="8aV-w1-NXE" secondAttribute="leading" id="OjU-Pl-gu1"/>
111+ <constraint firstItem="oAV-GW-iea" firstAttribute="trailing" secondItem="8aV-w1-NXE" secondAttribute="trailing" id="bPj-q1-pY6"/>
112+ <constraint firstItem="oAV-GW-iea" firstAttribute="top" secondItem="8aV-w1-NXE" secondAttribute="top" id="gUc-es-YHL"/>
113+ <constraint firstItem="a4v-ng-lm7" firstAttribute="bottom" secondItem="8aV-w1-NXE" secondAttribute="bottom" id="hZH-fU-zM5"/>
114+ <constraint firstItem="oAV-GW-iea" firstAttribute="leading" secondItem="8aV-w1-NXE" secondAttribute="leading" id="mos-8B-wjS"/>
115+ <constraint firstItem="a4v-ng-lm7" firstAttribute="trailing" secondItem="8aV-w1-NXE" secondAttribute="trailing" id="ntK-1r-AUW"/>
116+ <constraint firstItem="a4v-ng-lm7" firstAttribute="top" secondItem="oAV-GW-iea" secondAttribute="bottom" id="siv-Xa-Nl5"/>
117+ </constraints>
118+ <viewLayoutGuide key="safeArea" id="8aV-w1-NXE"/>
119+ </view>
120+ <connections>
121+ <outlet property="heightConstraint" destination="uRW-2Q-OyM" id="fmn-uI-ooi"/>
122+ <outlet property="mapContainerView" destination="oAV-GW-iea" id="jmP-q0-WbS"/>
123+ <outlet property="phoneContentView" destination="a4v-ng-lm7" id="W1O-A7-6EP"/>
124+ <outlet property="phoneTextField" destination="wBC-oa-ekQ" id="Btg-A3-tpm"/>
125+ </connections>
126+ </viewController>
127+ <placeholder placeholderIdentifier="IBFirstResponder" id="hNg-aQ-v7C" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
128+ </objects>
129+ <point key="canvasLocation" x="1664.8" y="158.77061469265368"/>
130+ </scene>
26131 </scenes>
27132</document>
Hookah Tau/Controllers/Admin/Reservations/AdminReservationsController.swiftdeleted−136
@@ -1,136 +0,0 @@
1−//
2−// AdminReservationsController.swift
3−// Hookah Tau
4−//
5−// Created by cstore on 19/12/2019.
6−// Copyright © 2019 Daria Rednikina. All rights reserved.
7−//
8−
9−import UIKit
10−
11−class AdminReservationsController: BaseViewController {
12−
13− // MARK:- Properties
14−
15− /// Establishment id
16− var id: Int?
17−
18− var tableView: UITableView? = {
19− let tableView = UITableView()
20− tableView.backgroundColor = .white
21− return tableView
22− }()
23−
24− var contentView: UIView = {
25− let view = UIView()
26− view.translatesAutoresizingMaskIntoConstraints = false
27− return view
28− }()
29−
30− var noReservationsView: AdminNoReservationsView?
31−
32− var reservationsService: ReservationsService?
33−
34− var activeReservations: [ReservationWithUser] = [] {
35− didSet {
36− guard let empty = noReservationsView else { return }
37− if activeReservations.isEmpty {
38− contentView.bringSubviewToFront(empty)
39− empty.alpha = 1
40− } else {
41− contentView.sendSubviewToBack(empty)
42− tableView?.alpha = 1
43− }
44−
45− tableView?.reloadData()
46− }
47− }
48−
49− override func viewDidLoad() {
50− super.viewDidLoad()
51−
52− reservationsService = ReservationsService(apiClient: APIClient.shared)
53− setUpContentView()
54− setupNavBar()
55− }
56−
57− override func viewDidAppear(_ animated: Bool) {
58− super.viewDidAppear(animated)
59−
60− performUpdate()
61− }
62−
63− // MARK:- Setup
64−
65− func setupNavBar() {
66− self.navigationItem.title = "Брони"
67− navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(book))
68− }
69−
70− func setUpContentView() {
71− self.view.addSubviewThatFills(contentView)
72−
73− // empty
74− noReservationsView = AdminNoReservationsView.loadFromNib()
75− contentView.addSubviewThatFills(noReservationsView)
76− noReservationsView?.bookButton?.addTarget(self, action: #selector(book), for: .touchUpInside)
77−
78− noReservationsView?.alpha = 0
79−
80− // table
81− contentView.addSubviewThatFills(tableView)
82−
83− setUpTableView()
84− }
85−
86− func setUpTableView() {
87− tableView?.delegate = self
88− tableView?.dataSource = self
89−
90− tableView?.alpha = 0
91−
92− tableView?.register(AdminReservationCell.self, forCellReuseIdentifier: adminReservationCellId)
93− }
94−
95− func performUpdate() {
96−
97− guard let id = id else { return }
98−
99− reservationsService?.getAdminReservations(establishmentId: id, completion: { [weak self] (result) in
100− switch result {
101− case .success(let reservations):
102− self?.activeReservations = reservations
103− case .failure(let err):
104− self?.displayAlert(forError: err)
105− }
106− })
107− }
108−
109− // MARK:- Button handler
110−
111− @objc func book() {
112−
113− }
114−
115−}
116−
117−
118−// MARK: - Table view data source
119−
120−extension AdminReservationsController: UITableViewDelegate, UITableViewDataSource {
121−
122− func numberOfSections(in tableView: UITableView) -> Int {
123− return 1
124− }
125−
126− func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
127− return activeReservations.count
128− }
129−
130− func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
131− let cell = tableView.dequeueReusableCell(withIdentifier: adminReservationCellId, for: indexPath) as! AdminReservationCell
132−
133− cell.bind(withModel: activeReservations[indexPath.row])
134− return cell
135− }
136−}
Hookah Tau/Controllers/Admin/Reservations/AdminReservationsCoordinator.swiftdeleted−22
@@ -1,22 +0,0 @@
1−//
2−// AdminReservationsCoordinator.swift
3−// Hookah Tau
4−//
5−// Created by cstore on 19/12/2019.
6−// Copyright © 2019 Daria Rednikina. All rights reserved.
7−//
8−
9−import UIKit
10−
11−class AdminReservationsCoordinator: BaseCoordinator {
12−
13− var establishmentId: Int?
14−
15− override func start() {
16− let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
17− let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationsController") as! AdminReservationsController
18− vc.id = establishmentId
19−
20− navigationController?.pushViewController(vc, animated: true)
21− }
22−}
Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsController.swiftnew file+219
@@ -0,0 +1,219 @@
1+//
2+// AdminReservationsController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 19/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminReservationsController: BaseViewController {
12+
13+ // MARK:- Properties
14+
15+ /// Establishment id
16+ var id: Int?
17+
18+ var tableView: UITableView? = {
19+ let tableView = UITableView()
20+ tableView.backgroundColor = .white
21+ return tableView
22+ }()
23+
24+ var contentView: UIView = {
25+ let view = UIView()
26+ view.translatesAutoresizingMaskIntoConstraints = false
27+ return view
28+ }()
29+
30+ var noReservationsView: AdminNoReservationsView?
31+
32+ var reservationsService: ReservationsService?
33+
34+ weak var coordinator: AdminReservationsCoordinator?
35+
36+ var refrControl: UIRefreshControl?
37+
38+ var activityIndicator: UIActivityIndicatorView?
39+
40+ var activeReservations: [ReservationWithUser] = [] {
41+ didSet {
42+ guard let empty = noReservationsView else { return }
43+ if activeReservations.isEmpty {
44+ contentView.bringSubviewToFront(empty)
45+ empty.alpha = 1
46+ } else {
47+ contentView.sendSubviewToBack(empty)
48+ tableView?.alpha = 1
49+ }
50+ activityIndicator?.stopAnimating()
51+ tableView?.reloadData()
52+ }
53+ }
54+
55+ override func viewDidLoad() {
56+ super.viewDidLoad()
57+
58+ reservationsService = ReservationsService(apiClient: APIClient.shared)
59+ setupRefreshControl()
60+ setUpContentView()
61+ setupNavBar()
62+ }
63+
64+ override func viewDidAppear(_ animated: Bool) {
65+ super.viewDidAppear(animated)
66+
67+ performUpdate()
68+ tabBarController?.tabBar.isHidden = false
69+ }
70+
71+ // MARK:- Setup
72+
73+ func setupNavBar() {
74+ self.navigationItem.title = "Брони"
75+ navigationController?.navigationBar.prefersLargeTitles = true
76+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(book))
77+ }
78+
79+ func setUpContentView() {
80+ self.view.addSubviewThatFills(contentView)
81+
82+ activityIndicator = UIActivityIndicatorView()
83+ contentView.addSubviewThatFills(activityIndicator)
84+ activityIndicator?.startAnimating()
85+
86+ // empty
87+ noReservationsView = AdminNoReservationsView.loadFromNib()
88+ contentView.addSubviewThatFills(noReservationsView)
89+ noReservationsView?.bookButton?.addTarget(self, action: #selector(book), for: .touchUpInside)
90+
91+ noReservationsView?.alpha = 0
92+
93+ // table
94+ contentView.addSubviewThatFills(tableView)
95+
96+ setUpTableView()
97+ }
98+
99+ func setUpTableView() {
100+ tableView?.delegate = self
101+ tableView?.dataSource = self
102+
103+ tableView?.alpha = 0
104+
105+ tableView?.register(AdminReservationCell.self, forCellReuseIdentifier: adminReservationCellId)
106+ }
107+
108+ func performUpdate() {
109+
110+ guard let id = id else { return }
111+
112+ reservationsService?.getAdminReservations(establishmentId: id, completion: { [weak self] (result) in
113+ switch result {
114+ case .success(let reservations):
115+ self?.activeReservations = reservations
116+ case .failure(let err):
117+ self?.displayAlert(forError: err)
118+ }
119+ self?.refrControl?.endRefreshing()
120+ })
121+ }
122+
123+ // MARK:- Button handler
124+
125+ @objc func book() {
126+ guard let establishmentId = id else { return }
127+ coordinator?.book(inEstablishment: establishmentId)
128+ }
129+
130+ func deleteReservation(uuid: String, completion: @escaping (([ReservationWithUser]?) -> Void)) {
131+ reservationsService?.deleteReservation(isAdmin: true, uuid: uuid) { result in
132+ if result {
133+ let newReservations = self.activeReservations.filter { $0.uuid != uuid }
134+ completion(newReservations)
135+ return
136+ }
137+
138+ completion(nil)
139+ }
140+ }
141+
142+ // MARK: - Refresh control
143+
144+ @objc func refreshData( _ ff: UIRefreshControl) {
145+ performUpdate()
146+ }
147+
148+ func setupRefreshControl() {
149+ refrControl = UIRefreshControl()
150+ tableView?.refreshControl = refrControl
151+ refrControl?.addTarget(self, action: #selector(refreshData(_:)), for: .valueChanged)
152+ }
153+
154+}
155+
156+
157+// MARK: - Table view data source
158+
159+extension AdminReservationsController: UITableViewDelegate, UITableViewDataSource {
160+
161+ func numberOfSections(in tableView: UITableView) -> Int {
162+ return 1
163+ }
164+
165+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
166+ return activeReservations.count
167+ }
168+
169+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
170+ let cell = tableView.dequeueReusableCell(withIdentifier: adminReservationCellId, for: indexPath) as! AdminReservationCell
171+
172+ cell.bind(withModel: activeReservations[indexPath.row])
173+ return cell
174+ }
175+
176+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
177+ let reservation = activeReservations[indexPath.row]
178+ coordinator?.seeExistingReservation(data: reservation)
179+ }
180+
181+ // Dimesions
182+
183+ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
184+ return UITableView.automaticDimension
185+ }
186+
187+ func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
188+ return 180.0
189+ }
190+
191+ // Update
192+
193+ func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
194+ return true
195+ }
196+
197+ func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
198+
199+ guard let uuid = activeReservations[indexPath.row].uuid else { return [] }
200+ let cancelButton = UITableViewRowAction(style: .normal, title: "отменить бронь") { _,_ in
201+
202+ self.deleteReservation(uuid: uuid) { optionalNewVal in
203+ if let newval = optionalNewVal {
204+ self.tableView?.beginUpdates()
205+ self.tableView?.deleteRows(at: [indexPath], with: .automatic)
206+ self.activeReservations = newval
207+ self.tableView?.endUpdates()
208+ } else {
209+ self.displayAlert(with: "Не удалось удалить вашу бронь! Попробуйте еще раз!")
210+ }
211+ }
212+ }
213+
214+ cancelButton.backgroundColor = .black
215+
216+ return [cancelButton]
217+ }
218+
219+}
Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsCoordinator.swiftnew file+54
@@ -0,0 +1,54 @@
1+//
2+// AdminReservationsCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 19/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminReservationsCoordinator: BaseCoordinator {
12+
13+ // MARK: - Properties
14+
15+ var didFinish: (() -> Void)?
16+
17+ var establishmentId: Int?
18+
19+ // MARK: - Lifecycle
20+
21+ override func start() {
22+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
23+ let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationsController") as! AdminReservationsController
24+ vc.id = establishmentId
25+ vc.coordinator = self
26+
27+ navigationController?.pushViewController(vc, animated: true)
28+ }
29+
30+ func seeExistingReservation(data: ReservationWithUser) {
31+ let reservationCoordinator = AdminReservationEditingCoordinator(navigationController: navigationController)
32+ addDependency(reservationCoordinator)
33+ reservationCoordinator.reservationData = data
34+ reservationCoordinator.didEndFlow = { [weak self] in
35+ self?.removeDependency(reservationCoordinator)
36+ self?.navigationController?.popViewController(animated: true)
37+ }
38+ reservationCoordinator.start()
39+ }
40+
41+ /// Сюда мы идем после нажатия кнопочки "Забронировать" или плюсика справа сверху
42+ func book(inEstablishment establishmentId: Int) {
43+ let reservationCoordinator = AdminFirstStepReservationViewCoordinator(navigationController: navigationController)
44+ reservationCoordinator.establishment = establishmentId
45+ addDependency(reservationCoordinator)
46+ reservationCoordinator.didFinish = { [weak self] in
47+ self?.navigationController?.popViewController(animated: false)
48+ self?.removeDependency(reservationCoordinator)
49+ self?.didFinish?()
50+ }
51+
52+ reservationCoordinator.start()
53+ }
54+}
Hookah Tau/Controllers/Admin/Reservations/Cell/AdminReservationCellView.swift → Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.swiftrenamed

The content is the same.

Hookah Tau/Controllers/Admin/Reservations/Cell/AdminReservationCellView.xib → Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.xibrenamed+11−21
@@ -12,14 +12,14 @@
1212 <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
1313 <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
1414 <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="AdminReservationCellView" customModule="Hookah_Tau" customModuleProvider="target">
15− <rect key="frame" x="0.0" y="0.0" width="414" height="136"/>
15+ <rect key="frame" x="0.0" y="0.0" width="414" height="180"/>
1616 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1717 <subviews>
18− <stackView opaque="NO" contentMode="scaleToFill" spacing="160" translatesAutoresizingMaskIntoConstraints="NO" id="au9-nK-XbR">
19− <rect key="frame" x="17" y="3" width="380" height="133"/>
18+ <stackView opaque="NO" contentMode="scaleToFill" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="au9-nK-XbR">
19+ <rect key="frame" x="20" y="20" width="374" height="140"/>
2020 <subviews>
2121 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="top" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="rCf-m8-E3w">
22− <rect key="frame" x="0.0" y="0.0" width="102" height="133"/>
22+ <rect key="frame" x="0.0" y="0.0" width="248" height="140"/>
2323 <subviews>
2424 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Стасик" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KV8-UG-2ws">
2525 <rect key="frame" x="0.0" y="0.0" width="54" height="19.5"/>
@@ -28,7 +28,7 @@
2828 <nil key="highlightedColor"/>
2929 </label>
3030 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="nsQ-SE-3vW">
31− <rect key="frame" x="0.0" y="43.5" width="102" height="45"/>
31+ <rect key="frame" x="0.0" y="47" width="102" height="45"/>
3232 <subviews>
3333 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="27 марта" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9k3-E5-XKR">
3434 <rect key="frame" x="0.0" y="0.0" width="76.5" height="20.5"/>
@@ -45,7 +45,7 @@
4545 </subviews>
4646 </stackView>
4747 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3 человека" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VhL-qH-zM9">
48− <rect key="frame" x="0.0" y="112.5" width="89" height="20.5"/>
48+ <rect key="frame" x="0.0" y="119.5" width="89" height="20.5"/>
4949 <fontDescription key="fontDescription" type="system" pointSize="17"/>
5050 <nil key="textColor"/>
5151 <nil key="highlightedColor"/>
@@ -53,27 +53,17 @@
5353 </subviews>
5454 </stackView>
5555 <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="1_7" translatesAutoresizingMaskIntoConstraints="NO" id="Oa6-n7-joV">
56− <rect key="frame" x="262" y="0.0" width="118" height="133"/>
56+ <rect key="frame" x="256" y="0.0" width="118" height="140"/>
5757 </imageView>
5858 </subviews>
5959 </stackView>
60− <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3u6-xl-ioO">
61− <rect key="frame" x="20" y="135" width="394" height="1"/>
62− <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
63− <constraints>
64− <constraint firstAttribute="height" constant="1" id="YCb-84-2qo"/>
65− </constraints>
66− </view>
6760 </subviews>
6861 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
6962 <constraints>
70− <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="au9-nK-XbR" secondAttribute="trailing" constant="17" id="Afx-VC-Cb6"/>
71− <constraint firstItem="3u6-xl-ioO" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="Lpw-A9-AcM"/>
72− <constraint firstItem="au9-nK-XbR" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="3" id="P5E-WG-AYe"/>
73− <constraint firstItem="au9-nK-XbR" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="Z6h-RB-tvj"/>
74− <constraint firstItem="au9-nK-XbR" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="17" id="fJE-La-qC6"/>
75− <constraint firstItem="3u6-xl-ioO" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="pqq-kW-AFb"/>
76− <constraint firstItem="3u6-xl-ioO" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="zrd-Ir-hU2"/>
63+ <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="au9-nK-XbR" secondAttribute="trailing" constant="20" id="Afx-VC-Cb6"/>
64+ <constraint firstItem="au9-nK-XbR" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="20" id="P5E-WG-AYe"/>
65+ <constraint firstItem="au9-nK-XbR" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" constant="-20" id="Z6h-RB-tvj"/>
66+ <constraint firstItem="au9-nK-XbR" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="fJE-La-qC6"/>
7767 </constraints>
7868 <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
7969 <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/1/AdminFirstStepReservationViewCoordinator.swiftnew file+108
@@ -0,0 +1,108 @@
1+//
2+// AdminFirstStepReservationViewCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 13/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+enum UserType {
12+ case admin
13+ case client
14+}
15+
16+protocol FirstStepReservationProtocol: BaseCoordinator {
17+
18+ var user: UserType { get }
19+
20+ func makeReservation(model: SecondStepModel, mapView: MapImageScroll?)
21+}
22+
23+class AdminFirstStepReservationViewCoordinator: BaseCoordinator{
24+
25+ // MARK: - Properties
26+
27+ var didFinish: (() -> Void)?
28+
29+ var establishment: Int = 1
30+
31+ var phone: String?
32+
33+ var fullUser: FullUser?
34+
35+ var model: SecondStepModel?
36+
37+ // MARK: - Lifecycle
38+
39+ override func start() {
40+ let storyboard = UIStoryboard(name: "Reservation", bundle: nil)
41+ let reservationVC = storyboard.instantiateViewController(withIdentifier: "ReservationViewController") as! FirstStepReservationViewController
42+
43+ reservationVC.coordinator = self
44+ reservationVC.establishmentId = establishment
45+
46+ navigationController?.pushViewController(reservationVC, animated: true)
47+ }
48+
49+
50+}
51+
52+extension AdminFirstStepReservationViewCoordinator: FirstStepReservationProtocol {
53+
54+ var user: UserType {
55+ get {
56+ return .admin
57+ }
58+ }
59+
60+ func makeReservation(model: SecondStepModel, mapView: MapImageScroll?) {
61+ self.model = model
62+
63+ if let p = phone {
64+ goToFinalStep(with: fullUser, phone: p)
65+ } else {
66+ goToSecondStep(model: model, mapView: mapView)
67+ }
68+ }
69+
70+ func goToFinalStep(with user: FullUser?, phone phoneNumber: String) {
71+ let finalStepCoordinator = AdminFinalStepReservationCoordinator(navigationController: navigationController)
72+ finalStepCoordinator.model = self.model
73+
74+ addDependency(finalStepCoordinator)
75+ finalStepCoordinator.didFinish = { [weak self] in
76+ self?.navigationController?.popViewController(animated: false)
77+ self?.removeDependency(finalStepCoordinator)
78+ self?.didFinish?()
79+ }
80+
81+ if let u = user {
82+ finalStepCoordinator.user = u
83+ } else {
84+ finalStepCoordinator.user = FullUser(uuid: nil,
85+ name: nil,
86+ phoneNumber: phoneNumber,
87+ isAdmin: true)
88+ }
89+
90+ finalStepCoordinator.start()
91+ }
92+
93+ func goToSecondStep(model: SecondStepModel, mapView: MapImageScroll?) {
94+ let reservationCoordinaotor = AdminSecondStepReservationViewCoordinator(navigationController: navigationController)
95+ reservationCoordinaotor.model = model
96+ reservationCoordinaotor.map = mapView
97+
98+ addDependency(reservationCoordinaotor)
99+ reservationCoordinaotor.didFinish = { [weak self] in
100+ self?.navigationController?.popViewController(animated: false)
101+ self?.removeDependency(reservationCoordinaotor)
102+ self?.didFinish?()
103+ }
104+
105+ reservationCoordinaotor.start()
106+ }
107+}
108+
Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/2/AdminSecondStepReservationViewController.swiftnew file+124
@@ -0,0 +1,124 @@
1+//
2+// AdminSecondStepReservationViewController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 13/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminSecondStepReservationViewController: BaseViewController {
12+
13+ // MARK: - IBAOutlets
14+
15+ @IBOutlet weak var mapContainerView: UIView!
16+
17+ @IBOutlet weak var heightConstraint: NSLayoutConstraint!
18+
19+ @IBOutlet weak var phoneTextField: PhoneTextField!
20+
21+ @IBOutlet weak var phoneContentView: UIView!
22+
23+ // MARK: - Properties
24+
25+ var table: Int?
26+
27+ var mapScrollView: MapImageScroll?
28+
29+ var clientsService: ClientsService?
30+
31+ weak var coordinator: AdminSecondStepReservationViewCoordinator?
32+
33+ let continueButton: Button = {
34+ let button = Button()
35+ let style = BlackButtonStyle()
36+ style.apply(to: button, withTitle: "ДАЛЕЕ")
37+ return button
38+ }()
39+
40+ let cancelButton: Button = {
41+ let button = Button()
42+ let style = BlackButtonStyle()
43+ style.apply(to: button, withTitle: "ОТМЕНИТЬ")
44+ return button
45+ }()
46+
47+ // MARK: - Lifecycle
48+
49+ override func viewDidLoad() {
50+ super.viewDidLoad()
51+
52+ setupMap()
53+ setupButtons()
54+ setUpKeyboard()
55+
56+ clientsService = ClientsService(apiClient: APIClient.shared)
57+ phoneTextField.becomeFirstResponder()
58+ }
59+
60+ // MARK: - Set up
61+
62+ func setupMap() {
63+ guard let t = table else { return }
64+ mapScrollView?.scrollToTable(table: t)
65+ mapContainerView.addSubviewThatFills(mapScrollView)
66+ }
67+
68+ func setupButtons() {
69+ addStackViewWithButtons(leftBtn: cancelButton,
70+ rightBtn: continueButton,
71+ constant: -phoneContentView.frame.height - 20)
72+
73+ cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside)
74+ continueButton.addTarget(self, action: #selector(continueBooking), for: .touchUpInside)
75+ }
76+
77+ // MARK: - Keyboad
78+
79+ @objc
80+ override func handleKeyboardNotifications(notification: NSNotification) {
81+ if let userInfo = notification.userInfo {
82+ let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
83+ heightConstraint?.constant = notification.name == UIResponder.keyboardWillShowNotification ? keyBoardFrame.height + view.safeAreaInsets.bottom + 120.5 : 120.5
84+ bottomConstraint?.constant = notification.name == UIResponder.keyboardWillShowNotification ? -keyBoardFrame.height + view.safeAreaInsets.bottom - 140.5 : -140.5
85+
86+ UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
87+ self.view.layoutIfNeeded()
88+ })
89+ }
90+ }
91+
92+ // MARK: - Button handlers
93+
94+ @objc func cancel() {
95+ coordinator?.didEndFlow?()
96+ }
97+
98+ @objc func continueBooking() {
99+ continueButton.loading = true
100+
101+ guard let phone = phoneTextField.text?.toApiPhoneNumberFormat(), phone.count == 9 else {
102+ displayAlert(with: "Сначала укажи номер!")
103+ return
104+ }
105+
106+ clientsService?.getClientByPhoneNumber(userPhone: phone, completion: { [unowned self] (res) in
107+ switch res {
108+ case .failure(let err):
109+ switch err {
110+ case .notFound:
111+ self.coordinator?.continueReservation(with: nil, phone: phone)
112+ default:
113+ self.displayAlert(with: "Что-то пошло не так! Попробуй еще раз :)")
114+ }
115+ case .success(let user):
116+ self.coordinator?.continueReservation(with: user, phone: phone)
117+ }
118+
119+ self.continueButton.loading = false
120+ })
121+ }
122+
123+
124+}
Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/2/AdminSecondStepReservationViewCoordinator.swiftnew file+54
@@ -0,0 +1,54 @@
1+//
2+// AdminSecondStepReservationViewCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 13/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminSecondStepReservationViewCoordinator: BaseCoordinator {
12+
13+ // MARK: - Properties
14+
15+ var didFinish: (() -> Void)?
16+
17+ var map: MapImageScroll?
18+
19+ var model: SecondStepModel?
20+
21+ // MARK: - Lifecycle
22+
23+ override func start() {
24+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
25+ let reservationVC = storyboard.instantiateViewController(withIdentifier: "AdminSecondStepReservationViewController") as! AdminSecondStepReservationViewController
26+ reservationVC.mapScrollView = map
27+ reservationVC.table = model?.table
28+ reservationVC.coordinator = self
29+ navigationController?.pushViewController(reservationVC, animated: true)
30+ }
31+
32+ func continueReservation(with user: FullUser?, phone phoneNumber: String) {
33+ let finalStepCoordinator = AdminFinalStepReservationCoordinator(navigationController: navigationController)
34+ finalStepCoordinator.model = self.model
35+
36+ addDependency(finalStepCoordinator)
37+ finalStepCoordinator.didFinish = { [weak self] in
38+ self?.navigationController?.popViewController(animated: false)
39+ self?.removeDependency(finalStepCoordinator)
40+ self?.didFinish?()
41+ }
42+
43+ if let u = user {
44+ finalStepCoordinator.user = u
45+ } else {
46+ finalStepCoordinator.user = FullUser(uuid: nil,
47+ name: nil,
48+ phoneNumber: phoneNumber,
49+ isAdmin: true)
50+ }
51+
52+ finalStepCoordinator.start()
53+ }
54+}
Hookah Tau/Controllers/Admin/Reservations/Making a reeservation/3/AdminFinalStepReservationCoordinator.swiftnew file+47
@@ -0,0 +1,47 @@
1+//
2+// AdminFinalStepReservationCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 14/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+protocol ShowReservationCoordinator: BaseCoordinator {
12+ var didFinish: (() -> Void)? { get }
13+}
14+
15+class AdminFinalStepReservationCoordinator: BaseCoordinator, ShowReservationCoordinator {
16+
17+ // MARK: - Properties
18+
19+ var didFinish: (() -> Void)?
20+
21+ var user: FullUser?
22+
23+ var model: SecondStepModel?
24+
25+ // MARK: - Lifecycle
26+
27+ override func start() {
28+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
29+ let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationEditingViewController") as! AdminReservationEditingViewController
30+
31+ guard
32+ let model = model,
33+ let u = user else { return }
34+ vc.reservationData = ReservationWithUser(uuid: nil,
35+ establishment: model.establishment,
36+ startTime: model.startTime,
37+ endTime: model.startTime.addHours(2),
38+ numberOfGuests: 2,
39+ reservedTable: model.table,
40+ owner: u)
41+
42+ vc.reservedPeriods = model.reservedintervals
43+ vc.coordinator = self
44+
45+ navigationController?.pushViewController(vc, animated: true)
46+ }
47+}
Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.swift+298−7
@@ -21,24 +21,65 @@ class AdminReservationView: UIView {
2121 @IBOutlet weak var cancelButton: Button? {
2222 didSet {
2323 let style = BlackButtonStyle()
24− style.apply(to: cancelButton)
24+ style.apply(to: cancelButton, withTitle: "ОТМЕНИТЬ")
2525 }
2626 }
2727
2828 @IBOutlet weak var nameLabel: UILabel?
2929
30− @IBOutlet weak var callButton: Button? {
30+ @IBOutlet weak var callButton: Button?
31+
32+ @IBOutlet weak var numberOfGuestLabel: UILabel?
33+
34+ @IBOutlet weak var guestSlider: UISlider?
35+
36+ @IBOutlet weak var intervalsContainerView: UIView?
37+
38+ @IBOutlet weak var scrollView: UIScrollView!
39+
40+ @IBOutlet weak var intervalsStackView: UIStackView!
41+
42+ @IBOutlet weak var saveButton: Button? {
3143 didSet {
3244 let style = BlackButtonStyle()
33− style.apply(to: callButton)
45+ style.apply(to: saveButton, withTitle: "СОХРАНИТЬ")
3446 }
3547 }
3648
37− @IBOutlet weak var numberOfGuestLabel: UILabel?
49+ // MARK: - Properties
3850
39− @IBOutlet weak var guestSlider: UISlider?
51+ // констрейнты начала и конца интервала
52+ var startLeadingConstraint: NSLayoutConstraint!
53+ var endLeadingConstraint: NSLayoutConstraint!
4054
41− @IBOutlet weak var intervalsContainerView: UIView?
55+ var startPoint: UIImageView = {
56+ var view = UIImageView()
57+ view.isUserInteractionEnabled = true
58+ view.image = #imageLiteral(resourceName: "handle_left")
59+ view.translatesAutoresizingMaskIntoConstraints = false
60+ return view
61+ }()
62+
63+ var endPoint: UIImageView = {
64+ var view = UIImageView()
65+ view.isUserInteractionEnabled = true
66+ view.image = #imageLiteral(resourceName: "Group")
67+ view.translatesAutoresizingMaskIntoConstraints = false
68+ return view
69+ }()
70+
71+ /// Интервал
72+ var fillInterval: UIView = {
73+ var view = UIView()
74+ view.backgroundColor = #colorLiteral(red: 0, green: 0.5921568627, blue: 1, alpha: 0.3426262843)
75+ view.translatesAutoresizingMaskIntoConstraints = false
76+ return view
77+ }()
78+
79+ /// Время начала вертелки.
80+ /// Для клиента это 12:00 смены (надо вызвать метод для получения текущей смены).
81+ /// Для администратора это `t` - 24h
82+ var startDate: Date?
4283
4384 // MARK:- Properties
4485
@@ -48,10 +89,46 @@ class AdminReservationView: UIView {
4889 /// Обновляем каждый раз когда меняется время и дата
4990 var data: ReservationData?
5091
92+ var model: ReservationWithUser?
93+
5194 // MARK: - Setup
5295
53− func bind(withModel model: ReservationData) {
96+ func bind(withModel model: ReservationWithUser) {
97+ let style = BlackButtonStyle()
98+ style.apply(to: callButton, withTitle: model.owner.phoneNumber.formattedNumber())
99+
100+
101+ let (date, time) = Date.format(model.startTime, model.endTime)
102+ dateLabel?.text = date
103+ timeLabel?.text = time
104+
105+ tableLabel?.text = "\(model.reservedTable) столик"
106+ nameLabel?.text = "\(model.owner.name ?? "❔")"
107+
108+ data = ReservationData(establishment: model.establishment,
109+ startTime: model.startTime,
110+ endTime: model.endTime,
111+ numberOfGuests: model.numberOfGuests,
112+ reservedTable: model.reservedTable)
113+ self.model = model
114+
115+ let (start, _) = model.owner.isAdmin ? model.startTime.getAdminStartingAndEndPoint() : model.startTime.getClientStartingAndEndPoint()
54116
117+ startDate = start
118+
119+ setUpIntervals()
120+
121+ guestSlider?.maximumValue = Float((TotalStorage.standard.getTable(establishment: model.establishment, table: model.reservedTable)?.maxGuestNumber ?? 2) + 1)
122+ }
123+
124+ func bind(withIntervals intervals: [ReservationPeriod]) {
125+ guard
126+ let data = data,
127+ let isAdmin = model?.owner.isAdmin
128+ else { return }
129+
130+ setUpReservedIntervals(intervals, isAdmin)
131+ setUpBookingPeriod(data.startTime, data.endTime)
55132 }
56133
57134 func updateDateAndTime(withData d: ReservationData?) {
@@ -61,6 +138,220 @@ class AdminReservationView: UIView {
61138 dateLabel?.text = date
62139 timeLabel?.text = time
63140 }
141+
142+ @IBAction func valueChanged(_ sender: UISlider) {
143+ let value = Int(sender.value)
144+
145+ if sender.value == sender.maximumValue {
146+ numberOfGuestLabel?.text = "Количество гостей: \(Int(value) - 1)+"
147+ } else {
148+ numberOfGuestLabel?.text = "Количество гостей: \(value)"
149+ }
150+
151+ guard let d = data else { return }
152+
153+ data = ReservationData(establishment: d.establishment,
154+ startTime: d.startTime,
155+ endTime: d.endTime,
156+ numberOfGuests: value,
157+ reservedTable: d.reservedTable)
158+ }
159+
160+
161+ func getNumberOfCalians(_ sliderValue: Int) -> String {
162+ return "Минимальное количество кальянов: \(sliderValue > 6 ? 2 : 1)"
163+ }
164+
165+ // MARK: - Setup
166+
167+ /// Тут происходит установка интервалов. Разное для клиента и админа
168+ func setUpIntervals() {
169+ guard let startPoint = startDate else { return }
170+
171+ let startingPoint = startPoint.getAdminsStartingPoint()
172+ let endingPoint = Constants.adminDuration
173+
174+ for period in startingPoint...endingPoint {
175+ let timePointView = TimePointView.loadFromNib()
176+ timePointView?.setUp(withTimePoint: period)
177+ let view = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: 106))
178+ view.addSubviewThatFills(timePointView)
179+ intervalsStackView.addArrangedSubview(view)
180+ }
181+ }
182+
183+
184+ private func setUpBookingPeriod(_ startTime: Date, _ endTime: Date? = nil) {
185+ self.scrollView.addSubview(startPoint)
186+ self.scrollView.addSubview(endPoint)
187+ self.scrollView.addSubview(fillInterval)
188+
189+ guard let startDate = startDate else { return }
190+
191+ let startConstr = CGFloat(startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
192+
193+ scrollView.setContentOffset(CGPoint(x: startConstr - 50, y: 0), animated: true)
194+
195+ startLeadingConstraint = startPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
196+ constant: startConstr)
197+
198+ if let end = endTime {
199+ let endConstr = CGFloat(end.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
200+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
201+ constant: endConstr)
202+ } else {
203+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
204+ constant:startConstr + Constants.minBookingLength)
205+ }
206+
207+ NSLayoutConstraint.activate([
208+
209+ startPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
210+ endPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
211+
212+ startPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
213+ endPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
214+
215+ startPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
216+ endPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
217+
218+ startLeadingConstraint,
219+ endLeadingConstraint,
220+
221+ fillInterval.leftAnchor.constraint(equalTo: startPoint.rightAnchor, constant: 0),
222+ fillInterval.rightAnchor.constraint(equalTo: endPoint.leftAnchor, constant: 0),
223+
224+ fillInterval.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
225+ fillInterval.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0)
226+ ])
227+
228+ let scrollViewPanStartGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanStart(recognizer:)))
229+ startPoint.addGestureRecognizer(scrollViewPanStartGesture)
230+
231+ let scrollViewPanEndGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanEnd(recognizer:)))
232+ endPoint.addGestureRecognizer(scrollViewPanEndGesture)
233+ }
234+
235+ func setUpReservedIntervals(_ reservations: [ReservationPeriod], _ isAdmin: Bool) {
236+
237+ for reservation in reservations {
238+ let uiview = UIView(frame: CGRect(x: 0, y: 0, width: Constants.widthTimePoint * reservation.duration, height: Constants.heightIntervals))
239+ uiview.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 0.5)
240+
241+
242+ uiview.translatesAutoresizingMaskIntoConstraints = false
243+ scrollView.addSubview(uiview)
244+
245+ guard let startDate = startDate else { return }
246+
247+ let s = reservation.startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
248+ let d = reservation.duration * Constants.widthTimePoint
249+
250+ let startTime = s < 0 ? 0 : s
251+
252+ let total = (isAdmin ? Constants.adminDuration : Constants.clientDuration) * Constants.widthTimePoint
253+ var duration = 0
254+ if s < 0 {
255+ duration = reservation.endTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
256+ } else {
257+ duration = (s + d) > total ? total - s : d
258+ }
259+
260+ NSLayoutConstraint.activate([
261+ uiview.bottomAnchor.constraint(equalToSystemSpacingBelow: scrollView.bottomAnchor, multiplier: 0),
262+ uiview.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
263+ constant: CGFloat(Constants.widthTimePoint / 2 + startTime)),
264+ uiview.widthAnchor.constraint(equalToConstant: CGFloat(duration)),
265+ uiview.heightAnchor.constraint(equalToConstant: CGFloat(Constants.heightIntervals))
266+ ])
267+ }
268+ }
269+
270+ // MARK: - Pan
271+
272+ @objc func handlePanStart(recognizer:UIPanGestureRecognizer) {
273+
274+ let translation = recognizer.translation(in: self.intervalsStackView)
275+ let duration = abs(startLeadingConstraint.constant + translation.x - endLeadingConstraint.constant)
276+
277+ if let view = recognizer.view, duration >= Constants.minBookingLength {
278+ startLeadingConstraint.constant += translation.x
279+
280+ changeTime(startLeadingConstraint, .start)
281+
282+ view.setNeedsUpdateConstraints()
283+ }
284+
285+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
286+ }
287+
288+ @objc func handlePanEnd(recognizer:UIPanGestureRecognizer) {
289+
290+ let translation = recognizer.translation(in: self.intervalsStackView)
291+ let duration = abs(startLeadingConstraint.constant - (endLeadingConstraint.constant + translation.x))
292+
293+ if let view = recognizer.view, duration >= Constants.minBookingLength {
294+ endLeadingConstraint.constant += translation.x
295+
296+ changeTime(endLeadingConstraint, .end)
297+ view.setNeedsUpdateConstraints()
298+ }
299+
300+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
301+ }
302+
303+ func changeTime(_ constraint: NSLayoutConstraint, _ period: Period) {
304+ guard let startDate = startDate, let d = data else { return }
305+
306+ let offset = Int(constraint.constant) % Constants.widthTimePoint
307+
308+ if offset == 0 {
309+ let p = Int(constraint.constant) / Constants.widthTimePoint
310+ let time = startDate.addPeriods(p)
311+
312+ switch period {
313+ case .start:
314+ data = ReservationData(establishment: d.establishment,
315+ startTime: time,
316+ endTime: d.endTime,
317+ numberOfGuests: d.numberOfGuests,
318+ reservedTable: d.reservedTable)
319+ case .end:
320+ data = ReservationData(establishment: d.establishment,
321+ startTime: d.startTime,
322+ endTime: time,
323+ numberOfGuests: d.numberOfGuests,
324+ reservedTable: d.reservedTable)
325+ }
326+
327+ updateDateAndTime(withData: data)
328+ }
329+ }
330+}
331+
332+// MARK: - Nested types
333+
334+extension AdminReservationView {
335+ enum Constants {
336+ static let widthTimePoint = 11
337+
338+ static let adminDuration = 288
339+ static let clientDuration = 90
340+ // 12:00 = 12 * 6
341+ static let clientStartingPoint = 72
342+
343+ static let pointWidth: CGFloat = 19
344+ static let pointHeight: CGFloat = 85
345+
346+ static let minBookingLength: CGFloat = 132
347+
348+ static let heightIntervals = 85
349+ }
350+
351+ enum Period {
352+ case start
353+ case end
354+ }
64355}
65356
66357
Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.xib+121−35
@@ -5,27 +5,26 @@
55 <deployment identifier="iOS"/>
66 <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8− <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
98 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
109 </dependencies>
1110 <objects>
1211 <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
1312 <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
1413 <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="AdminReservationView" customModule="Hookah_Tau" customModuleProvider="target">
15− <rect key="frame" x="0.0" y="0.0" width="414" height="441"/>
14+ <rect key="frame" x="0.0" y="0.0" width="414" height="385"/>
1615 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1716 <subviews>
18− <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="48" translatesAutoresizingMaskIntoConstraints="NO" id="AEf-R4-K1F">
19− <rect key="frame" x="21" y="21" width="372" height="399"/>
17+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="24" translatesAutoresizingMaskIntoConstraints="NO" id="AEf-R4-K1F">
18+ <rect key="frame" x="0.0" y="16" width="414" height="349"/>
2019 <subviews>
21− <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="top" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="oh7-ka-ctS">
22− <rect key="frame" x="0.0" y="0.0" width="372" height="138.5"/>
20+ <stackView opaque="NO" contentMode="scaleToFill" alignment="top" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="oh7-ka-ctS">
21+ <rect key="frame" x="21" y="0.0" width="372" height="136.5"/>
2322 <subviews>
24− <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="22" translatesAutoresizingMaskIntoConstraints="NO" id="IY6-F4-aln">
25− <rect key="frame" x="0.0" y="0.0" width="109" height="136.5"/>
23+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="22" translatesAutoresizingMaskIntoConstraints="NO" id="IY6-F4-aln">
24+ <rect key="frame" x="0.0" y="0.0" width="129" height="136.5"/>
2625 <subviews>
2726 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="hEV-kW-bBH">
28− <rect key="frame" x="0.0" y="0.0" width="109" height="77.5"/>
27+ <rect key="frame" x="0.0" y="0.0" width="129" height="77.5"/>
2928 <subviews>
3029 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="7 столик" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qNR-EO-oMK">
3130 <rect key="frame" x="0.0" y="0.0" width="93" height="26.5"/>
@@ -39,13 +38,17 @@
3938 <nil key="textColor"/>
4039 <nil key="highlightedColor"/>
4140 </label>
42− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3:00 — 5:00" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gau-aS-vtT">
43− <rect key="frame" x="0.0" y="57" width="102.5" height="20.5"/>
41+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3:00 — 5:00ffff" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gau-aS-vtT">
42+ <rect key="frame" x="0.0" y="57" width="129" height="20.5"/>
4443 <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
4544 <nil key="textColor"/>
4645 <nil key="highlightedColor"/>
4746 </label>
4847 </subviews>
48+ <constraints>
49+ <constraint firstAttribute="trailing" secondItem="gau-aS-vtT" secondAttribute="trailing" id="OYu-7e-8pX"/>
50+ <constraint firstItem="gau-aS-vtT" firstAttribute="leading" secondItem="hEV-kW-bBH" secondAttribute="leading" id="egK-i8-r2N"/>
51+ </constraints>
4952 </stackView>
5053 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iiD-uf-IDp" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
5154 <rect key="frame" x="0.0" y="99.5" width="109" height="37"/>
@@ -57,22 +60,41 @@
5760 <state key="normal" title="Button"/>
5861 </button>
5962 </subviews>
63+ <constraints>
64+ <constraint firstAttribute="trailing" secondItem="hEV-kW-bBH" secondAttribute="trailing" id="7MI-YT-uHi"/>
65+ <constraint firstAttribute="width" constant="129" id="BQh-sd-jbL"/>
66+ <constraint firstItem="hEV-kW-bBH" firstAttribute="leading" secondItem="IY6-F4-aln" secondAttribute="leading" id="tsb-PN-YGB"/>
67+ </constraints>
6068 </stackView>
61− <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="bottom" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="59c-nG-5Fh">
62− <rect key="frame" x="198" y="0.0" width="174" height="71.5"/>
69+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="bottom" spacing="28" translatesAutoresizingMaskIntoConstraints="NO" id="Kjb-P4-v2B">
70+ <rect key="frame" x="133" y="0.0" width="239" height="136.5"/>
6371 <subviews>
64− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Стасик" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OoG-F4-OVu">
65− <rect key="frame" x="95.5" y="0.0" width="78.5" height="26.5"/>
66− <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
67− <nil key="textColor"/>
68− <nil key="highlightedColor"/>
69− </label>
70− <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xkd-xQ-cFd" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
71− <rect key="frame" x="0.0" y="34.5" width="174" height="37"/>
72+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="bottom" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="59c-nG-5Fh">
73+ <rect key="frame" x="65" y="0.0" width="174" height="71.5"/>
74+ <subviews>
75+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Стасик" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OoG-F4-OVu">
76+ <rect key="frame" x="95.5" y="0.0" width="78.5" height="26.5"/>
77+ <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
78+ <nil key="textColor"/>
79+ <nil key="highlightedColor"/>
80+ </label>
81+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Xkd-xQ-cFd" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
82+ <rect key="frame" x="0.0" y="34.5" width="174" height="37"/>
83+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
84+ <constraints>
85+ <constraint firstAttribute="height" constant="37" id="7Wj-WF-xzD"/>
86+ <constraint firstAttribute="width" constant="174" id="ZYo-ko-rwn"/>
87+ </constraints>
88+ <state key="normal" title="Button"/>
89+ </button>
90+ </subviews>
91+ </stackView>
92+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="npC-AF-3pj" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
93+ <rect key="frame" x="103" y="99.5" width="136" height="37"/>
7294 <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
7395 <constraints>
74− <constraint firstAttribute="height" constant="37" id="7Wj-WF-xzD"/>
75− <constraint firstAttribute="width" constant="174" id="ZYo-ko-rwn"/>
96+ <constraint firstAttribute="width" constant="136" id="jg5-lP-Ekd"/>
97+ <constraint firstAttribute="height" constant="37" id="w98-8a-pmH"/>
7698 </constraints>
7799 <state key="normal" title="Button"/>
78100 </button>
@@ -80,43 +102,104 @@
80102 </stackView>
81103 </subviews>
82104 </stackView>
83− <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="9RO-aK-yNh">
84− <rect key="frame" x="0.0" y="186.5" width="372" height="58.5"/>
105+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="9RO-aK-yNh">
106+ <rect key="frame" x="21" y="160.5" width="372" height="58.5"/>
85107 <subviews>
86108 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Количество гостей: 2" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yTM-U5-XjI">
87− <rect key="frame" x="0.0" y="0.0" width="181" height="20.5"/>
109+ <rect key="frame" x="0.0" y="0.0" width="372" height="20.5"/>
88110 <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
89111 <nil key="textColor"/>
90112 <nil key="highlightedColor"/>
91113 </label>
92114 <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="2" minValue="1" maxValue="8" translatesAutoresizingMaskIntoConstraints="NO" id="Lqw-9Z-34d">
93115 <rect key="frame" x="-2" y="28.5" width="376" height="31"/>
116+ <connections>
117+ <action selector="valueChanged:" destination="iN0-l3-epB" eventType="valueChanged" id="OwE-Nq-PHY"/>
118+ </connections>
94119 </slider>
95120 </subviews>
96121 </stackView>
97122 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vKS-ia-RxW">
98− <rect key="frame" x="0.0" y="293" width="372" height="106"/>
99− <color key="backgroundColor" red="0.34509803919999998" green="0.33725490200000002" blue="0.83921568629999999" alpha="0.33104130993150682" colorSpace="custom" customColorSpace="sRGB"/>
123+ <rect key="frame" x="0.0" y="243" width="414" height="106"/>
124+ <subviews>
125+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EiP-QO-PbU">
126+ <rect key="frame" x="0.0" y="20" width="414" height="85"/>
127+ <subviews>
128+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OpX-io-LgH">
129+ <rect key="frame" x="0.0" y="0.0" width="414" height="1"/>
130+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
131+ <constraints>
132+ <constraint firstAttribute="height" constant="1" id="k2D-mj-31b"/>
133+ </constraints>
134+ </view>
135+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fOe-cM-riJ">
136+ <rect key="frame" x="0.0" y="84" width="414" height="1"/>
137+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
138+ <constraints>
139+ <constraint firstAttribute="height" constant="1" id="A2B-su-3Hi"/>
140+ </constraints>
141+ </view>
142+ </subviews>
143+ <color key="backgroundColor" red="0.8901960784313725" green="0.8901960784313725" blue="0.8901960784313725" alpha="0.6951519692" colorSpace="calibratedRGB"/>
144+ <constraints>
145+ <constraint firstItem="OpX-io-LgH" firstAttribute="top" secondItem="EiP-QO-PbU" secondAttribute="top" id="73d-6U-4Jc"/>
146+ <constraint firstAttribute="height" constant="85" id="JMc-tA-wHe"/>
147+ <constraint firstAttribute="trailing" secondItem="fOe-cM-riJ" secondAttribute="trailing" id="JVQ-ub-pEy"/>
148+ <constraint firstItem="OpX-io-LgH" firstAttribute="leading" secondItem="EiP-QO-PbU" secondAttribute="leading" id="hBd-hJ-yz7"/>
149+ <constraint firstAttribute="bottom" secondItem="fOe-cM-riJ" secondAttribute="bottom" id="im0-y1-nC8"/>
150+ <constraint firstItem="fOe-cM-riJ" firstAttribute="leading" secondItem="EiP-QO-PbU" secondAttribute="leading" id="qH9-Ww-nd5"/>
151+ <constraint firstAttribute="trailing" secondItem="OpX-io-LgH" secondAttribute="trailing" id="ueR-ZM-ELi"/>
152+ </constraints>
153+ </view>
154+ <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="trF-VM-0qV">
155+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
156+ <subviews>
157+ <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bpJ-pe-tQf">
158+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
159+ </stackView>
160+ </subviews>
161+ <constraints>
162+ <constraint firstAttribute="bottom" secondItem="bpJ-pe-tQf" secondAttribute="bottom" id="BxY-h0-LHi"/>
163+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="leading" secondItem="trF-VM-0qV" secondAttribute="leading" id="Ec5-Lb-NsX"/>
164+ <constraint firstAttribute="trailing" secondItem="bpJ-pe-tQf" secondAttribute="trailing" id="d0a-8z-CE6"/>
165+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="width" secondItem="trF-VM-0qV" secondAttribute="width" priority="250" id="mYw-sK-G8B"/>
166+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="height" secondItem="trF-VM-0qV" secondAttribute="height" id="wdB-oa-hvt"/>
167+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="top" secondItem="trF-VM-0qV" secondAttribute="top" id="x6e-gw-nXA"/>
168+ </constraints>
169+ <viewLayoutGuide key="contentLayoutGuide" id="QAB-NG-eb4"/>
170+ <viewLayoutGuide key="frameLayoutGuide" id="xUW-w2-Jj1"/>
171+ </scrollView>
172+ </subviews>
173+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
100174 <constraints>
175+ <constraint firstAttribute="bottom" secondItem="trF-VM-0qV" secondAttribute="bottom" id="4kT-64-W5t"/>
176+ <constraint firstItem="trF-VM-0qV" firstAttribute="top" secondItem="vKS-ia-RxW" secondAttribute="top" id="M4X-6f-0WX"/>
177+ <constraint firstItem="trF-VM-0qV" firstAttribute="leading" secondItem="vKS-ia-RxW" secondAttribute="leading" id="ZXK-Vm-o6S"/>
178+ <constraint firstAttribute="trailing" secondItem="trF-VM-0qV" secondAttribute="trailing" id="a2R-kF-NcS"/>
101179 <constraint firstAttribute="height" constant="106" id="ebp-l7-k9Z"/>
180+ <constraint firstItem="EiP-QO-PbU" firstAttribute="leading" secondItem="vKS-ia-RxW" secondAttribute="leading" id="fHc-26-5lu"/>
181+ <constraint firstAttribute="bottom" secondItem="EiP-QO-PbU" secondAttribute="bottom" constant="1" id="kxj-VY-srA"/>
182+ <constraint firstAttribute="trailing" secondItem="EiP-QO-PbU" secondAttribute="trailing" id="sbQ-sE-GAh"/>
102183 </constraints>
103184 </view>
104185 </subviews>
105186 <constraints>
106187 <constraint firstItem="vKS-ia-RxW" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" id="0cO-AV-PJL"/>
107− <constraint firstAttribute="trailing" secondItem="oh7-ka-ctS" secondAttribute="trailing" id="KeE-6F-h5x"/>
188+ <constraint firstAttribute="trailing" secondItem="oh7-ka-ctS" secondAttribute="trailing" constant="21" id="KeE-6F-h5x"/>
189+ <constraint firstAttribute="trailing" secondItem="9RO-aK-yNh" secondAttribute="trailing" constant="21" id="Kh6-4l-QJO"/>
108190 <constraint firstAttribute="bottom" secondItem="vKS-ia-RxW" secondAttribute="bottom" id="TKW-xo-b61"/>
109− <constraint firstItem="oh7-ka-ctS" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" id="mJ2-gh-vCg"/>
191+ <constraint firstItem="9RO-aK-yNh" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" constant="21" id="Teg-kp-nZt"/>
192+ <constraint firstItem="oh7-ka-ctS" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" constant="21" id="mJ2-gh-vCg"/>
110193 <constraint firstAttribute="trailing" secondItem="vKS-ia-RxW" secondAttribute="trailing" id="uDV-uM-jby"/>
111194 </constraints>
112195 </stackView>
113196 </subviews>
114197 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
115198 <constraints>
116− <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="AEf-R4-K1F" secondAttribute="bottom" constant="21" id="4MN-Yl-hYv"/>
117− <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="AEf-R4-K1F" secondAttribute="trailing" constant="21" id="C1h-bA-S6p"/>
118− <constraint firstItem="AEf-R4-K1F" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="21" id="H1o-ua-Tnn"/>
119− <constraint firstItem="vUN-kp-3ea" firstAttribute="top" secondItem="AEf-R4-K1F" secondAttribute="top" constant="-21" id="eYI-is-YLt"/>
199+ <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="AEf-R4-K1F" secondAttribute="trailing" id="C1h-bA-S6p"/>
200+ <constraint firstItem="AEf-R4-K1F" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="H1o-ua-Tnn"/>
201+ <constraint firstAttribute="top" secondItem="AEf-R4-K1F" secondAttribute="top" constant="-16" id="eYI-is-YLt"/>
202+ <constraint firstAttribute="bottom" secondItem="AEf-R4-K1F" secondAttribute="bottom" constant="20" id="fdG-zZ-LeV"/>
120203 </constraints>
121204 <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
122205 <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
@@ -126,12 +209,15 @@
126209 <outlet property="dateLabel" destination="KHM-Yj-bBa" id="iS8-eN-OFW"/>
127210 <outlet property="guestSlider" destination="Lqw-9Z-34d" id="cEj-JF-J4D"/>
128211 <outlet property="intervalsContainerView" destination="vKS-ia-RxW" id="0Ig-rh-hXD"/>
212+ <outlet property="intervalsStackView" destination="bpJ-pe-tQf" id="aF0-Qm-kuh"/>
129213 <outlet property="nameLabel" destination="OoG-F4-OVu" id="rnW-fG-jFs"/>
130214 <outlet property="numberOfGuestLabel" destination="yTM-U5-XjI" id="z0O-94-Mp7"/>
215+ <outlet property="saveButton" destination="npC-AF-3pj" id="Rfm-Zl-zES"/>
216+ <outlet property="scrollView" destination="trF-VM-0qV" id="P5v-fg-hUW"/>
131217 <outlet property="tableLabel" destination="qNR-EO-oMK" id="63P-AS-fgf"/>
132218 <outlet property="timeLabel" destination="gau-aS-vtT" id="7Iy-Cz-zdY"/>
133219 </connections>
134− <point key="canvasLocation" x="137.59999999999999" y="120.98950524737631"/>
220+ <point key="canvasLocation" x="137.59999999999999" y="95.802098950524751"/>
135221 </view>
136222 </objects>
137223</document>
Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.swift+5
@@ -12,6 +12,8 @@ class AdminNoReservationsView: UIView {
1212
1313 // MARK: - IBAOutlets
1414
15+ @IBOutlet weak var titleLabel: UILabel?
16+
1517 @IBOutlet weak var bookButton: Button? {
1618 didSet {
1719 let style = BlackButtonStyle()
@@ -19,4 +21,7 @@ class AdminNoReservationsView: UIView {
1921 }
2022 }
2123
24+ func setup(withTitle title: String) {
25+ titleLabel?.text = title
26+ }
2227}
Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.xib+1
@@ -46,6 +46,7 @@
4646 <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
4747 <connections>
4848 <outlet property="bookButton" destination="y3F-ua-mLb" id="aC4-gz-Gqd"/>
49+ <outlet property="titleLabel" destination="Svd-DB-3Zu" id="n8Z-Wq-vzC"/>
4950 </connections>
5051 <point key="canvasLocation" x="137.68115942028987" y="91.741071428571431"/>
5152 </view>
Hookah Tau/Controllers/Admin/Tab bar/Admin.storyboard+13−8
@@ -1,14 +1,14 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina6_1" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99 </dependencies>
1010 <scenes>
11− <!--Navigation Controller-->
11+ <!--Клиенты-->
1212 <scene sceneID="Q3i-Bo-7ns">
1313 <objects>
1414 <viewController id="Ifn-72-mgx" customClass="UINavigationController" sceneMemberID="viewController">
@@ -18,13 +18,13 @@
1818 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
1919 <viewLayoutGuide key="safeArea" id="yHG-bf-ndj"/>
2020 </view>
21− <tabBarItem key="tabBarItem" systemItem="downloads" id="9K1-uc-72H"/>
21+ <tabBarItem key="tabBarItem" title="Клиенты" image="peoplee" id="9K1-uc-72H"/>
2222 </viewController>
2323 <placeholder placeholderIdentifier="IBFirstResponder" id="8X9-ca-bCi" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
2424 </objects>
2525 <point key="canvasLocation" x="1061" y="-327"/>
2626 </scene>
27− <!--Navigation Controller-->
27+ <!--Профиль-->
2828 <scene sceneID="cAb-sw-24k">
2929 <objects>
3030 <viewController id="Jsm-cH-XbJ" customClass="UINavigationController" sceneMemberID="viewController">
@@ -34,13 +34,13 @@
3434 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
3535 <viewLayoutGuide key="safeArea" id="f96-UV-eoi"/>
3636 </view>
37− <tabBarItem key="tabBarItem" systemItem="contacts" id="ziF-T0-lg1"/>
37+ <tabBarItem key="tabBarItem" title="Профиль" image="person" id="ziF-T0-lg1"/>
3838 </viewController>
3939 <placeholder placeholderIdentifier="IBFirstResponder" id="qqi-vY-Mog" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
4040 </objects>
4141 <point key="canvasLocation" x="1061" y="433"/>
4242 </scene>
43− <!--Navigation Controller-->
43+ <!--Брони-->
4444 <scene sceneID="p6D-Gs-DUK">
4545 <objects>
4646 <viewController id="Tc3-UW-Fwi" customClass="UINavigationController" sceneMemberID="viewController">
@@ -50,7 +50,7 @@
5050 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
5151 <viewLayoutGuide key="safeArea" id="z8c-Ub-USh"/>
5252 </view>
53− <tabBarItem key="tabBarItem" systemItem="mostViewed" id="szW-oN-ixR"/>
53+ <tabBarItem key="tabBarItem" title="Брони" image="stack" id="szW-oN-ixR"/>
5454 </viewController>
5555 <placeholder placeholderIdentifier="IBFirstResponder" id="1ST-zr-4Zs" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
5656 </objects>
@@ -76,4 +76,9 @@
7676 <point key="canvasLocation" x="-817" y="-251"/>
7777 </scene>
7878 </scenes>
79+ <resources>
80+ <image name="peoplee" width="51" height="25"/>
81+ <image name="person" width="25" height="24"/>
82+ <image name="stack" width="25" height="24"/>
83+ </resources>
7984</document>
Hookah Tau/Controllers/BaseTableViewController.swiftnew file+28
@@ -0,0 +1,28 @@
1+//
2+// BaseTableViewController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 07/01/2020.
6+// Copyright © 2020 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class BaseTableViewController: UITableViewController {
12+
13+ // MARK: - Alert
14+
15+ func displayAlert(forError error: GeneralError = GeneralError.noData, with message: String? = nil) {
16+ var title: String? = message == nil ? error.localizedDescription : message
17+ switch error {
18+ case .serverError(let se):
19+ title = se.error
20+ fallthrough // да тоже произведение искусства
21+ default:
22+ let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
23+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
24+ self.present(alert, animated: true, completion: nil)
25+ }
26+ }
27+
28+}
Hookah Tau/Controllers/BaseViewController.swift+1−1
@@ -97,7 +97,7 @@ class BaseViewController: UIViewController {
9797 }
9898
9999 @objc
100− private func handleKeyboardNotifications(notification: NSNotification){
100+ func handleKeyboardNotifications(notification: NSNotification){
101101 if let userInfo = notification.userInfo {
102102 let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
103103 bottomConstraint?.constant = notification.name == UIResponder.keyboardWillShowNotification ? -keyBoardFrame.height + view.safeAreaInsets.bottom - 20 : 0
Hookah Tau/Controllers/Client/Profile/ProfileChangeViewController.swift+3−3
@@ -23,7 +23,7 @@ class ProfileChangeViewController: BaseViewController {
2323
2424 var changeView: ChangeView?
2525
26− var settingsService: SettingsService?
26+ var settingsService: ClientsService?
2727
2828 var contentView: UIView = {
2929 let view = UIView()
@@ -60,7 +60,7 @@ class ProfileChangeViewController: BaseViewController {
6060 setUpView()
6161 setUpKeyboard()
6262
63− settingsService = SettingsService(apiClient: APIClient.shared)
63+ settingsService = ClientsService(apiClient: APIClient.shared)
6464 }
6565
6666 func setUpView() {
@@ -106,7 +106,7 @@ class ProfileChangeViewController: BaseViewController {
106106 func addNewAdmin(phone: String) {
107107 doneButton.loading = true
108108
109− settingsService?.addOtherAdmin(phone: phone) { [weak self] res in
109+ settingsService?.changeAdmin(crud: CrudMethod.put, data: phone) { [weak self] res in
110110 if res {
111111 self?.coordinator?.update(withModel: nil)
112112 } else {
Hookah Tau/Controllers/Client/Reservations/Active Reservations/ReservationsViewController.swift+23−2
@@ -23,6 +23,7 @@ class ReservationsViewController: BaseViewController {
2323 tableView?.alpha = 1
2424 }
2525
26+ activityIndicator.stopAnimating()
2627 tableView?.reloadData()
2728 }
2829 }
@@ -45,6 +46,8 @@ class ReservationsViewController: BaseViewController {
4546 return tableView
4647 }()
4748
49+ var activityIndicator = UIActivityIndicatorView()
50+
4851 // MARK: - Lifecycle
4952
5053 override func viewDidLoad() {
@@ -74,6 +77,9 @@ class ReservationsViewController: BaseViewController {
7477 func setUpContentView() {
7578 self.view.addSubviewThatFills(contentView)
7679
80+ contentView.addSubviewThatFills(activityIndicator)
81+ activityIndicator.startAnimating()
82+
7783 // empty
7884 noReservationsView = NoReservationsView.loadFromNib()
7985 contentView.addSubviewThatFills(noReservationsView)
@@ -85,6 +91,7 @@ class ReservationsViewController: BaseViewController {
8591 contentView.addSubviewThatFills(tableView)
8692
8793 setUpTableView()
94+ setupRefreshControl()
8895 }
8996
9097 func setUpTableView() {
@@ -120,11 +127,12 @@ class ReservationsViewController: BaseViewController {
120127 case .failure(let err):
121128 self?.displayAlert(forError: err)
122129 }
130+ self?.refrControl?.endRefreshing()
123131 })
124132 }
125133
126134 func deleteReservation(uuid: String, completion: @escaping (([Reservation]?) -> Void)) {
127− reservationsService?.deleteReservation(uuid: uuid) { result in
135+ reservationsService?.deleteReservation(isAdmin: false, uuid: uuid) { result in
128136 if result {
129137 let newReservations = self.activeReservations.filter { $0.uuid != uuid }
130138 completion(newReservations)
@@ -135,6 +143,19 @@ class ReservationsViewController: BaseViewController {
135143 }
136144 }
137145
146+ // MARK: - Refresh control
147+
148+ var refrControl: UIRefreshControl?
149+
150+ func setupRefreshControl() {
151+ refrControl = UIRefreshControl()
152+ tableView?.refreshControl = refrControl
153+ refrControl?.addTarget(self, action: #selector(refreshData(_:)), for: .valueChanged)
154+ }
155+
156+ @objc func refreshData( _ ff: UIRefreshControl) {
157+ performUpdate()
158+ }
138159}
139160
140161extension ReservationsViewController: UITableViewDelegate, UITableViewDataSource {
@@ -171,7 +192,7 @@ extension ReservationsViewController: UITableViewDelegate, UITableViewDataSource
171192 func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
172193
173194 let uuid = activeReservations[indexPath.row].uuid
174− let cancelButton = UITableViewRowAction(style: .normal, title: "❌") { _,_ in
195+ let cancelButton = UITableViewRowAction(style: .normal, title: "отменить бронь") { _,_ in
175196
176197 self.deleteReservation(uuid: uuid) { optionalNewVal in
177198 if let newval = optionalNewVal {
Hookah Tau/Controllers/Client/Reservations/Maps/1/MapView1.xib+14−14
@@ -1,9 +1,9 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina4_7" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99 </dependencies>
@@ -21,22 +21,22 @@
2121 <rect key="frame" x="0.0" y="0.0" width="536" height="636"/>
2222 </imageView>
2323 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Y23-Wu-4BW" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
24− <rect key="frame" x="44" y="37" width="102" height="117"/>
24+ <rect key="frame" x="44" y="37" width="102" height="119"/>
2525 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
2626 <constraints>
2727 <constraint firstAttribute="width" constant="102" id="EKZ-Ny-4RQ"/>
28− <constraint firstAttribute="height" constant="117" id="u1N-5d-cFR"/>
28+ <constraint firstAttribute="height" constant="119" id="u1N-5d-cFR"/>
2929 </constraints>
3030 <connections>
3131 <action selector="chooseFirstTable:" destination="iN0-l3-epB" eventType="touchUpInside" id="1O9-7Q-a8g"/>
3232 </connections>
3333 </button>
3434 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xsw-fJ-8cN" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
35− <rect key="frame" x="209" y="113" width="131" height="119"/>
35+ <rect key="frame" x="209" y="116" width="131" height="117"/>
3636 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
3737 <constraints>
3838 <constraint firstAttribute="width" constant="131" id="2xi-Oz-ceC"/>
39− <constraint firstAttribute="height" constant="119" id="xPD-fh-9Zg"/>
39+ <constraint firstAttribute="height" constant="117" id="xPD-fh-9Zg"/>
4040 </constraints>
4141 <connections>
4242 <action selector="chooseSecondTable:" destination="iN0-l3-epB" eventType="touchUpInside" id="5XE-gZ-DyJ"/>
@@ -54,10 +54,10 @@
5454 </connections>
5555 </button>
5656 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fw9-l6-N8a" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
57− <rect key="frame" x="60" y="350" width="102" height="118"/>
57+ <rect key="frame" x="60" y="350" width="102" height="120"/>
5858 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
5959 <constraints>
60− <constraint firstAttribute="height" constant="118" id="2Vp-Ip-7cO"/>
60+ <constraint firstAttribute="height" constant="120" id="2Vp-Ip-7cO"/>
6161 <constraint firstAttribute="width" constant="102" id="2hI-75-UCM"/>
6262 </constraints>
6363 <connections>
@@ -65,10 +65,10 @@
6565 </connections>
6666 </button>
6767 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KJZ-Nl-7hH" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
68− <rect key="frame" x="222" y="272" width="104" height="119"/>
68+ <rect key="frame" x="222" y="273" width="104" height="120"/>
6969 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
7070 <constraints>
71− <constraint firstAttribute="height" constant="119" id="Ifu-kd-YHH"/>
71+ <constraint firstAttribute="height" constant="120" id="Ifu-kd-YHH"/>
7272 <constraint firstAttribute="width" constant="104" id="phm-aP-6dx"/>
7373 </constraints>
7474 <connections>
@@ -76,7 +76,7 @@
7676 </connections>
7777 </button>
7878 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mUw-im-1BC" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
79− <rect key="frame" x="222" y="431" width="92" height="67"/>
79+ <rect key="frame" x="222" y="433" width="92" height="67"/>
8080 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
8181 <constraints>
8282 <constraint firstAttribute="width" constant="92" id="AJ6-ku-4R3"/>
@@ -87,10 +87,10 @@
8787 </connections>
8888 </button>
8989 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aIM-Qn-Uhl" customClass="TableButton" customModule="Hookah_Tau" customModuleProvider="target">
90− <rect key="frame" x="386" y="116" width="130" height="119"/>
90+ <rect key="frame" x="386" y="116" width="131" height="119"/>
9191 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
9292 <constraints>
93− <constraint firstAttribute="width" constant="130" id="Lhq-jM-PQ0"/>
93+ <constraint firstAttribute="width" constant="131" id="Lhq-jM-PQ0"/>
9494 <constraint firstAttribute="height" constant="119" id="vsx-u8-L6o"/>
9595 </constraints>
9696 <connections>
@@ -123,7 +123,7 @@
123123 <constraint firstItem="mUw-im-1BC" firstAttribute="top" secondItem="KJZ-Nl-7hH" secondAttribute="bottom" constant="40" id="GaI-BE-NZy"/>
124124 <constraint firstItem="btD-Wl-3P6" firstAttribute="width" secondItem="FuE-LV-NBR" secondAttribute="width" priority="250" id="MDs-kk-aza"/>
125125 <constraint firstItem="btD-Wl-3P6" firstAttribute="top" secondItem="aIM-Qn-Uhl" secondAttribute="bottom" constant="-235" id="NwR-U0-V6F"/>
126− <constraint firstItem="mUw-im-1BC" firstAttribute="top" secondItem="btD-Wl-3P6" secondAttribute="bottom" constant="-205" id="UXz-DD-dPH"/>
126+ <constraint firstItem="mUw-im-1BC" firstAttribute="top" secondItem="btD-Wl-3P6" secondAttribute="bottom" constant="-203" id="UXz-DD-dPH"/>
127127 <constraint firstItem="mUw-im-1BC" firstAttribute="leading" secondItem="Fw9-l6-N8a" secondAttribute="trailing" constant="60" id="V4u-ko-iFV"/>
128128 <constraint firstItem="KJZ-Nl-7hH" firstAttribute="top" secondItem="xsw-fJ-8cN" secondAttribute="bottom" constant="40" id="XFV-l2-chC"/>
129129 <constraint firstItem="btD-Wl-3P6" firstAttribute="top" secondItem="onR-hl-Q2x" secondAttribute="bottom" constant="-332" id="XNT-i4-1l8"/>
Hookah Tau/Controllers/Client/Reservations/Maps/MapView.swift+1
@@ -33,6 +33,7 @@ class MapImageScroll: UIView {
3333 shadowView.alpha = 1
3434
3535 scrollViewParent.zoom(to: tableButton.frame, animated: true)
36+ scrollViewParent.contentOffset = CGPoint(x: scrollViewParent.contentOffset.x, y: scrollViewParent.contentOffset.y + 100)
3637 scrollViewParent.setZoomScale(zoomScale, animated: true)
3738 }
3839
Hookah Tau/Controllers/Client/Reservations/Reservation.storyboard+4−4
@@ -1,9 +1,9 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina6_1" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99 </dependencies>
@@ -49,10 +49,10 @@
4949 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
5050 <subviews>
5151 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NBJ-7w-vpV">
52− <rect key="frame" x="0.0" y="502" width="414" height="394"/>
52+ <rect key="frame" x="0.0" y="544" width="414" height="352"/>
5353 <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
5454 <constraints>
55− <constraint firstAttribute="height" constant="394" id="KKm-d5-Srx"/>
55+ <constraint firstAttribute="height" constant="352" id="KKm-d5-Srx"/>
5656 </constraints>
5757 </view>
5858 </subviews>
Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepReservationCoordinator.swift+8−1
@@ -8,7 +8,7 @@
88
99import UIKit
1010
11−class FirstStepReservationCoordinator: BaseCoordinator {
11+class FirstStepReservationCoordinator: BaseCoordinator, FirstStepReservationProtocol {
1212
1313 // MARK: - Properties
1414
@@ -28,6 +28,13 @@ class FirstStepReservationCoordinator: BaseCoordinator {
2828 navigationController?.pushViewController(reservationVC, animated: true)
2929 }
3030
31+ var user: UserType {
32+ get {
33+ return .client
34+ }
35+ }
36+
37+
3138 func makeReservation(model: SecondStepModel,
3239 mapView: MapImageScroll?) {
3340
Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepReservationViewController.swift+15−6
@@ -13,7 +13,7 @@ class FirstStepReservationViewController: BaseViewController {
1313
1414 // MARK: - Properties
1515
16− weak var coordinator: FirstStepReservationCoordinator?
16+ weak var coordinator: FirstStepReservationProtocol?
1717
1818 @IBOutlet weak var childContentView: UIView!
1919
@@ -82,12 +82,21 @@ class FirstStepReservationViewController: BaseViewController {
8282 }
8383
8484 func setupButtons() {
85− addStackViewWithButtons(leftBtn: callButton,
86− rightBtn: continueButton,
87− constant: -childContentView.frame.height - 20)
85+
86+ guard let coord = coordinator else { return }
87+
88+ if coord.user == .admin {
89+ addStackViewWithButtons(rightBtn: continueButton,
90+ constant: -childContentView.frame.height - 20)
91+ } else {
92+ addStackViewWithButtons(leftBtn: callButton,
93+ rightBtn: continueButton,
94+ constant: -childContentView.frame.height - 20)
95+
96+ callButton.addTarget(self, action: #selector(call), for: .touchUpInside)
97+ }
8898
8999 continueButton.addTarget(self, action: #selector(continueBooking), for: .touchUpInside)
90− callButton.addTarget(self, action: #selector(call), for: .touchUpInside)
91100 }
92101
93102 func performUpdate() {
@@ -178,7 +187,7 @@ extension FirstStepReservationViewController: MapUpdater {
178187 let areBooked = allReservations?.intervals[date] ?? []
179188
180189 let isAdmin = DataStorage.standard.getUserModel()?.isAdmin ?? false
181− let closed = isAdmin || date.isInClosingHours()
190+ let closed = !isAdmin && date.isInClosingHours()
182191
183192 if Set(areBooked).contains(tableId) || closed {
184193 continueButton.availiable = false
Hookah Tau/Controllers/Client/Reservations/Steps/1/FirstStepView.xib+18−18
@@ -1,9 +1,9 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3− <device id="retina4_7" orientation="portrait" appearance="light"/>
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3+ <device id="retina5_5" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
99 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -106,14 +106,14 @@
106106 </constraints>
107107 </view>
108108 <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="TriangleDown" translatesAutoresizingMaskIntoConstraints="NO" id="5dC-Lq-opV">
109− <rect key="frame" x="184.5" y="0.0" width="45" height="29"/>
109+ <rect key="frame" x="184.66666666666666" y="0.0" width="45" height="29"/>
110110 <constraints>
111111 <constraint firstAttribute="width" constant="45" id="Tsw-fS-8qs"/>
112112 <constraint firstAttribute="height" constant="29" id="YWD-ja-gD5"/>
113113 </constraints>
114114 </imageView>
115115 <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="TriangleUp" translatesAutoresizingMaskIntoConstraints="NO" id="Igw-O9-W92">
116− <rect key="frame" x="184.5" y="196" width="45" height="29"/>
116+ <rect key="frame" x="184.66666666666666" y="196" width="45" height="29"/>
117117 <constraints>
118118 <constraint firstAttribute="height" constant="29" id="AdE-yJ-WLL"/>
119119 <constraint firstAttribute="width" constant="45" id="Eaa-P8-3mv"/>
@@ -159,16 +159,16 @@
159159 <rect key="frame" x="0.0" y="29" width="414" height="59"/>
160160 <subviews>
161161 <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="OEF-Kc-d38">
162− <rect key="frame" x="8" y="5" width="398" height="14.5"/>
162+ <rect key="frame" x="8" y="5.0000000000000009" width="398" height="14.333333333333336"/>
163163 <subviews>
164164 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="November" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Mn-Mn-qaI">
165− <rect key="frame" x="0.0" y="0.0" width="58.5" height="14.5"/>
165+ <rect key="frame" x="0.0" y="0.0" width="58.333333333333336" height="14.333333333333334"/>
166166 <fontDescription key="fontDescription" type="system" pointSize="12"/>
167167 <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
168168 <nil key="highlightedColor"/>
169169 </label>
170170 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="January" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rfg-Ts-DX0">
171− <rect key="frame" x="353" y="0.0" width="45" height="14.5"/>
171+ <rect key="frame" x="353" y="0.0" width="45" height="14.333333333333334"/>
172172 <fontDescription key="fontDescription" type="system" pointSize="12"/>
173173 <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
174174 <nil key="highlightedColor"/>
@@ -176,7 +176,7 @@
176176 </subviews>
177177 </stackView>
178178 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="December" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fbc-VX-43f">
179− <rect key="frame" x="176.5" y="5" width="61" height="14.5"/>
179+ <rect key="frame" x="176.66666666666666" y="5.0000000000000009" width="60.666666666666657" height="14.333333333333336"/>
180180 <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="12"/>
181181 <color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
182182 <nil key="highlightedColor"/>
@@ -204,28 +204,28 @@
204204 <constraints>
205205 <constraint firstItem="Y9G-UI-WV7" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="22z-Ck-vsZ"/>
206206 <constraint firstItem="a6N-9A-8y5" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="5c8-YB-dSB"/>
207− <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="954-g3-FZD" secondAttribute="bottom" constant="37" id="C3k-Mb-wT4"/>
207+ <constraint firstAttribute="bottom" secondItem="954-g3-FZD" secondAttribute="bottom" constant="37" id="C3k-Mb-wT4"/>
208208 <constraint firstItem="Y9G-UI-WV7" firstAttribute="top" secondItem="5dC-Lq-opV" secondAttribute="bottom" id="E97-Lg-K2s"/>
209209 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="fvt-zf-8tU" secondAttribute="trailing" id="Eeh-zB-KWe"/>
210210 <constraint firstItem="a6N-9A-8y5" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="F03-yv-41V"/>
211211 <constraint firstItem="pgz-8G-Kx4" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="G0y-2f-SkS"/>
212212 <constraint firstItem="5dC-Lq-opV" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="If5-v7-UAj"/>
213− <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="pgz-8G-Kx4" secondAttribute="bottom" constant="137" id="Lgn-Nv-wjD"/>
213+ <constraint firstAttribute="bottom" secondItem="pgz-8G-Kx4" secondAttribute="bottom" constant="137" id="Lgn-Nv-wjD"/>
214214 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="954-g3-FZD" secondAttribute="trailing" id="MGt-d4-Win"/>
215215 <constraint firstItem="fvt-zf-8tU" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="SaI-rP-2em"/>
216216 <constraint firstItem="Igw-O9-W92" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="TtM-Bc-V1U"/>
217− <constraint firstItem="Igw-O9-W92" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="V9u-Eu-VME"/>
217+ <constraint firstItem="Igw-O9-W92" firstAttribute="bottom" secondItem="iN0-l3-epB" secondAttribute="bottom" id="V9u-Eu-VME"/>
218218 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="pgz-8G-Kx4" secondAttribute="trailing" id="Z6D-PY-Jrh"/>
219219 <constraint firstItem="Igw-O9-W92" firstAttribute="top" secondItem="a6N-9A-8y5" secondAttribute="bottom" constant="8" id="f4D-nq-HMa"/>
220220 <constraint firstItem="Y9G-UI-WV7" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="gZw-8a-dTG"/>
221221 <constraint firstItem="954-g3-FZD" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="itw-0z-3i7"/>
222− <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="fvt-zf-8tU" secondAttribute="bottom" constant="137" id="lOx-ke-O6a"/>
222+ <constraint firstAttribute="bottom" secondItem="fvt-zf-8tU" secondAttribute="bottom" constant="137" id="lOx-ke-O6a"/>
223223 <constraint firstItem="z9p-OK-tL7" firstAttribute="top" secondItem="52e-LM-cF6" secondAttribute="bottom" constant="9" id="ldj-Qh-p0k"/>
224− <constraint firstItem="fvt-zf-8tU" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="29" id="pSg-dd-INI"/>
225− <constraint firstItem="pgz-8G-Kx4" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="29" id="vfO-LG-tqR"/>
226− <constraint firstItem="954-g3-FZD" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="97" id="vhK-2E-j3k"/>
227− <constraint firstItem="5dC-Lq-opV" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" id="xOt-l0-Vzy"/>
228− <constraint firstItem="a6N-9A-8y5" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="97" id="yyf-QB-vfF"/>
224+ <constraint firstItem="fvt-zf-8tU" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="29" id="pSg-dd-INI"/>
225+ <constraint firstItem="pgz-8G-Kx4" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="29" id="vfO-LG-tqR"/>
226+ <constraint firstItem="954-g3-FZD" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="97" id="vhK-2E-j3k"/>
227+ <constraint firstItem="5dC-Lq-opV" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="xOt-l0-Vzy"/>
228+ <constraint firstItem="a6N-9A-8y5" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="97" id="yyf-QB-vfF"/>
229229 </constraints>
230230 <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
231231 <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
Hookah Tau/Controllers/Client/Reservations/Steps/2/SecondStepView.swift−7
@@ -23,8 +23,6 @@ class SecondStepView: UIView {
2323
2424 @IBOutlet weak var totalBookingLabel: UILabel!
2525
26− @IBOutlet weak var infoLabel: UILabel!
27−
2826 @IBOutlet weak var specialLabel: UILabel! {
2927 didSet {
3028 let m = NSMutableAttributedString(string: "Если Вам нужно что-то особенное, Вы можете связаться с администратором ")
@@ -131,7 +129,6 @@ class SecondStepView: UIView {
131129 @IBAction func sliderValueChanged(_ sender: UISlider) {
132130 let value = Int(sender.value)
133131 numberOfGuestsLabel.text = "Количество гостей: \(value)"
134− infoLabel.text = getNumberOfCalians(value)
135132
136133 guard let d = data else { return }
137134
@@ -151,10 +148,6 @@ class SecondStepView: UIView {
151148 UIApplication.shared.open(urlPhone)
152149 }
153150
154− func getNumberOfCalians(_ sliderValue: Int) -> String {
155− return "Минимальное количество кальянов: \(sliderValue > 6 ? 2 : 1)"
156− }
157−
158151 // MARK: - Setup
159152
160153 /// Тут происходит установка интервалов. Разное для клиента и админа
Hookah Tau/Controllers/Client/Reservations/Steps/2/SecondStepView.xib+12−19
@@ -1,9 +1,9 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina4_7" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
99 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -12,14 +12,14 @@
1212 <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
1313 <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
1414 <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="SecondStepView" customModule="Hookah_Tau" customModuleProvider="target">
15− <rect key="frame" x="0.0" y="0.0" width="414" height="394"/>
15+ <rect key="frame" x="0.0" y="0.0" width="414" height="352"/>
1616 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1717 <subviews>
1818 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="bJ1-1q-VXq">
19− <rect key="frame" x="20" y="20" width="374" height="131.5"/>
19+ <rect key="frame" x="20" y="20" width="374" height="80"/>
2020 <subviews>
2121 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="center" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="Dkh-B7-3Dl">
22− <rect key="frame" x="16.5" y="0.0" width="341.5" height="92.5"/>
22+ <rect key="frame" x="16.5" y="0.0" width="341.5" height="41"/>
2323 <subviews>
2424 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Забронировать 7 столик на 3 человека 29 марта с 3:00 до 5:30?" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7M9-lI-egu">
2525 <rect key="frame" x="0.0" y="0.0" width="341.5" height="41"/>
@@ -27,16 +27,10 @@
2727 <nil key="textColor"/>
2828 <nil key="highlightedColor"/>
2929 </label>
30− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Минимальное количество кальянов: 2" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zMP-Wa-N6b">
31− <rect key="frame" x="11" y="72" width="319" height="20.5"/>
32− <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
33− <nil key="textColor"/>
34− <nil key="highlightedColor"/>
35− </label>
3630 </subviews>
3731 </stackView>
3832 <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="PHb-Nt-9I2">
39− <rect key="frame" x="47" y="102.5" width="280.5" height="29"/>
33+ <rect key="frame" x="47" y="51" width="280.5" height="29"/>
4034 <string key="text">Если Вам нужно что-то особенное, Вы можете
4135связаться с администратором. </string>
4236 <fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="12"/>
@@ -46,7 +40,7 @@
4640 </subviews>
4741 </stackView>
4842 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="9c7-IZ-n2p">
49− <rect key="frame" x="20" y="171.5" width="373.5" height="60.5"/>
43+ <rect key="frame" x="20" y="120" width="373.5" height="70"/>
5044 <subviews>
5145 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Количество гостей: 3" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QuY-68-2ax">
5246 <rect key="frame" x="0.0" y="0.0" width="373.5" height="20.5"/>
@@ -55,7 +49,7 @@
5549 <nil key="highlightedColor"/>
5650 </label>
5751 <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="3" minValue="1" maxValue="10" translatesAutoresizingMaskIntoConstraints="NO" id="TrY-MC-Ty0">
58− <rect key="frame" x="-2" y="30.5" width="377.5" height="31"/>
52+ <rect key="frame" x="-2" y="30.5" width="377.5" height="40.5"/>
5953 <connections>
6054 <action selector="sliderValueChanged:" destination="iN0-l3-epB" eventType="valueChanged" id="ekZ-3h-EeB"/>
6155 </connections>
@@ -63,7 +57,7 @@
6357 </subviews>
6458 </stackView>
6559 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RJE-Hk-KNh">
66− <rect key="frame" x="0.0" y="252" width="414" height="106"/>
60+ <rect key="frame" x="0.0" y="210" width="414" height="106"/>
6761 <subviews>
6862 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dmc-2e-y3T">
6963 <rect key="frame" x="0.0" y="21" width="414" height="85"/>
@@ -129,7 +123,7 @@
129123 </subviews>
130124 <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
131125 <constraints>
132− <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="RJE-Hk-KNh" secondAttribute="bottom" constant="36" id="0W3-e2-hCU"/>
126+ <constraint firstAttribute="bottom" secondItem="RJE-Hk-KNh" secondAttribute="bottom" constant="36" id="0W3-e2-hCU"/>
133127 <constraint firstItem="9c7-IZ-n2p" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="94y-7J-ksb"/>
134128 <constraint firstItem="9c7-IZ-n2p" firstAttribute="top" secondItem="bJ1-1q-VXq" secondAttribute="bottom" constant="20" id="9TT-vO-7PZ"/>
135129 <constraint firstItem="RJE-Hk-KNh" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="AgS-kP-UNE"/>
@@ -137,21 +131,20 @@
137131 <constraint firstAttribute="trailing" secondItem="bJ1-1q-VXq" secondAttribute="trailing" constant="20" id="OCH-eC-kP5"/>
138132 <constraint firstItem="bJ1-1q-VXq" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="PCm-nY-M6p"/>
139133 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="9c7-IZ-n2p" secondAttribute="trailing" constant="20.5" id="Rh7-V2-v2u"/>
140− <constraint firstItem="bJ1-1q-VXq" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="20" id="SnG-Cy-hHq"/>
134+ <constraint firstItem="bJ1-1q-VXq" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="SnG-Cy-hHq"/>
141135 <constraint firstItem="RJE-Hk-KNh" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="fW0-Tt-MpN"/>
142136 </constraints>
143137 <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
144138 <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
145139 <connections>
146140 <outlet property="guestSlider" destination="TrY-MC-Ty0" id="mR0-wv-AM7"/>
147− <outlet property="infoLabel" destination="zMP-Wa-N6b" id="htu-ql-T0O"/>
148141 <outlet property="intervalsStackView" destination="IIO-je-eBt" id="gaP-LX-Xed"/>
149142 <outlet property="numberOfGuestsLabel" destination="QuY-68-2ax" id="DvK-dr-c3J"/>
150143 <outlet property="scrollView" destination="zk1-3E-2kS" id="b8R-jY-2Q3"/>
151144 <outlet property="specialLabel" destination="PHb-Nt-9I2" id="hXL-Je-dax"/>
152145 <outlet property="totalBookingLabel" destination="7M9-lI-egu" id="7YU-yI-Gm4"/>
153146 </connections>
154− <point key="canvasLocation" x="-8" y="70"/>
147+ <point key="canvasLocation" x="-8" y="88.15592203898052"/>
155148 </view>
156149 </objects>
157150</document>
Hookah Tau/Controllers/Client/Reservations/Steps/3/ThirdStepReservationViewController.swift+1−1
@@ -139,7 +139,7 @@ class ThirdStepReservationViewController: BaseViewController {
139139 guard let res = reservation else { return }
140140
141141 cancelButton.loading = true
142− reservationService?.deleteReservation(uuid: res.uuid, completion: { [weak self] (result) in
142+ reservationService?.deleteReservation(isAdmin: false, uuid: res.uuid, completion: { [weak self] (result) in
143143 self?.cancelButton.loading = false
144144
145145 if result {
Hookah Tau/Controllers/Client/Reservations/Steps/3/ThirdStepView.xib+3−3
@@ -1,9 +1,9 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina4_7" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
99 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -120,7 +120,7 @@
120120 <constraint firstItem="0H2-jJ-gOj" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="GR6-gI-ecm"/>
121121 <constraint firstItem="ByV-f1-mVO" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="KKj-7n-eQf"/>
122122 <constraint firstItem="0H2-jJ-gOj" firstAttribute="top" secondItem="DL4-0a-WxC" secondAttribute="bottom" constant="30" id="VbN-sb-Pko"/>
123− <constraint firstItem="DL4-0a-WxC" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="20" id="cm0-tz-ta4"/>
123+ <constraint firstItem="DL4-0a-WxC" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="20" id="cm0-tz-ta4"/>
124124 <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="0H2-jJ-gOj" secondAttribute="trailing" constant="20" id="i3D-sP-KlI"/>
125125 <constraint firstAttribute="trailing" secondItem="DL4-0a-WxC" secondAttribute="trailing" constant="20" id="kHp-8n-OTp"/>
126126 <constraint firstItem="DL4-0a-WxC" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="20" id="vOg-X1-y93"/>
Hookah Tau/Controllers/Client/Reservations/Steps/change /ChangeReservationViewController.swift+1
@@ -137,6 +137,7 @@ class ChangeReservationViewController: BaseViewController {
137137 numberOfGuests: r.numberOfGuests,
138138 endTime: r.endTime,
139139 uuid: uuid,
140+ isAdmin: false,
140141 completion: { [weak self] (res) in
141142 if res {
142143 self?.coordinator?.finish(Reservation(uuid: uuid, reservationData: r))
Hookah Tau/Controllers/Client/Tab bar/Client.storyboard+10−6
@@ -1,14 +1,14 @@
11<?xml version="1.0" encoding="UTF-8"?>
2−<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33 <device id="retina6_1" orientation="portrait" appearance="light"/>
44 <dependencies>
55 <deployment identifier="iOS"/>
6− <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
77 <capability name="Safe area layout guides" minToolsVersion="9.0"/>
88 <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
99 </dependencies>
1010 <scenes>
11− <!--Navigation Controller-->
11+ <!--Профиль-->
1212 <scene sceneID="b0t-Wu-VhQ">
1313 <objects>
1414 <viewController id="qqN-qg-VJ7" customClass="UINavigationController" sceneMemberID="viewController">
@@ -18,13 +18,13 @@
1818 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
1919 <viewLayoutGuide key="safeArea" id="WgY-fu-N23"/>
2020 </view>
21− <tabBarItem key="tabBarItem" systemItem="contacts" id="vPo-1j-5WO"/>
21+ <tabBarItem key="tabBarItem" title="Профиль" image="person" id="vPo-1j-5WO"/>
2222 </viewController>
2323 <placeholder placeholderIdentifier="IBFirstResponder" id="zv0-gE-V7c" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
2424 </objects>
2525 <point key="canvasLocation" x="-166" y="195"/>
2626 </scene>
27− <!--Navigation Controller-->
27+ <!--Брони-->
2828 <scene sceneID="nQv-Ks-RjM">
2929 <objects>
3030 <viewController id="MGt-zZ-AbL" customClass="UINavigationController" sceneMemberID="viewController">
@@ -34,7 +34,7 @@
3434 <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
3535 <viewLayoutGuide key="safeArea" id="vjZ-Ji-o7T"/>
3636 </view>
37− <tabBarItem key="tabBarItem" systemItem="mostViewed" id="B7T-zH-VfU"/>
37+ <tabBarItem key="tabBarItem" title="Брони" image="stack" id="B7T-zH-VfU"/>
3838 </viewController>
3939 <placeholder placeholderIdentifier="IBFirstResponder" id="W83-e1-iNk" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
4040 </objects>
@@ -59,4 +59,8 @@
5959 <point key="canvasLocation" x="-1081" y="-130"/>
6060 </scene>
6161 </scenes>
62+ <resources>
63+ <image name="person" width="25" height="24"/>
64+ <image name="stack" width="25" height="24"/>
65+ </resources>
6266</document>
Hookah Tau/HFunc.swift+7
@@ -15,6 +15,9 @@ class HFunc {
1515
1616 static let main = HFunc()
1717
18+ func exportClientsToCSV(_ arr: [Client]) -> String {
19+ return arr.reduce(Client.csvHeading) { return funcToMapClientsIntoString($0, $1) }
20+ }
1821
1922 /// Filtering function to get every periods which intersects with given date
2023 /// - Parameter periods: all periods of bookings (for a particular table)
@@ -58,4 +61,8 @@ class HFunc {
5861
5962 return { (period: ReservationPeriod) in intervalChecking(period.startTime, period.endTime) }
6063 }
64+
65+ private func funcToMapClientsIntoString(_ acc: String, _ el: Client) -> String {
66+ return acc + el.toString + "\n"
67+ }
6168}
Hookah Tau/Info.plist+5−3
@@ -2,8 +2,6 @@
22<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33<plist version="1.0">
44<dict>
5− <key>UIUserInterfaceStyle</key>
6− <string>Ligth</string>
75 <key>CFBundleDevelopmentRegion</key>
86 <string>$(DEVELOPMENT_LANGUAGE)</string>
97 <key>CFBundleExecutable</key>
@@ -19,7 +17,9 @@
1917 <key>CFBundleShortVersionString</key>
2018 <string>1.0</string>
2119 <key>CFBundleVersion</key>
22− <string>1</string>
20+ <string>$(CURRENT_PROJECT_VERSION)</string>
21+ <key>ITSAppUsesNonExemptEncryption</key>
22+ <false/>
2323 <key>LSRequiresIPhoneOS</key>
2424 <true/>
2525 <key>UILaunchStoryboardName</key>
@@ -39,5 +39,7 @@
3939 <string>UIInterfaceOrientationLandscapeLeft</string>
4040 <string>UIInterfaceOrientationLandscapeRight</string>
4141 </array>
42+ <key>UIUserInterfaceStyle</key>
43+ <string>Light</string>
4244</dict>
4345</plist>
Hookah Tau/Models/Reservation.swift+24−1
@@ -51,7 +51,7 @@ struct ReservationData: MyCodable {
5151/// Структура для получения респонса на `GET /admin/reservations/{establishment id}`
5252struct ReservationWithUser: MyCodable {
5353
54− var uuid: String
54+ var uuid: String?
5555 var establishment: Int
5656 var startTime: Date
5757 var endTime: Date
@@ -59,6 +59,29 @@ struct ReservationWithUser: MyCodable {
5959 var reservedTable: Int
6060 var owner: FullUser
6161
62+ init(reservation: Reservation, user: FullUser) {
63+ self.uuid = reservation.uuid
64+ self.establishment = reservation.establishment
65+ self.startTime = reservation.startTime
66+ self.endTime = reservation.endTime
67+ self.numberOfGuests = reservation.numberOfGuests
68+ self.reservedTable = reservation.reservedTable
69+ self.owner = user
70+ }
71+
72+ init(uuid: String?, establishment: Int, startTime: Date, endTime: Date, numberOfGuests: Int, reservedTable: Int, owner: FullUser) {
73+ self.uuid = uuid
74+ self.establishment = establishment
75+ self.startTime = startTime
76+ self.endTime = endTime
77+ self.numberOfGuests = numberOfGuests
78+ self.reservedTable = reservedTable
79+ self.owner = owner
80+ }
6281}
6382
83+struct AdminCreateReservation: MyCodable {
84+ var reservation: ReservationData
85+ var user: String
86+}
6487
Hookah Tau/Models/User.swift+19−1
@@ -15,10 +15,28 @@ struct User: MyCodable {
1515 var isAdmin: Bool
1616}
1717
18−/// При запросах на список пользователей в аккаунте администратора
18+/// При запросах на список пользователей в аккаунте администратора.
1919struct FullUser: MyCodable {
20+ var uuid: String?
21+ var name: String?
22+ var phoneNumber: String
23+ var isAdmin: Bool
24+}
25+
26+/// `GET /admin/clients`
27+struct Client: MyCodable {
2028 var uuid: String
2129 var name: String?
2230 var phoneNumber: String
2331 var isAdmin: Bool
32+ var reservationCount: Int
33+ var isBlocked: Bool
34+
35+ static var csvHeading: String {
36+ return "uuid, имя, телефон, админ, количество активных бронирований, в черном списке\n"
37+ }
38+
39+ var toString: String {
40+ return "\(uuid), \(name ?? "❔"), +79\(phoneNumber), \(isAdmin), \(reservationCount), \(isBlocked)"
41+ }
2442}
Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/1024.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/120-1.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/120.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/152.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/167.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/180.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/20.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/29.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-1.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40-2.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/40.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/58-1.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/58.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/60.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/76.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/80-1.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/80.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/87.pngnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json+36−18
@@ -1,93 +1,111 @@
11{
22 "images" : [
33 {
4− "idiom" : "iphone",
54 "size" : "20x20",
5+ "idiom" : "iphone",
6+ "filename" : "40-1.png",
67 "scale" : "2x"
78 },
89 {
9− "idiom" : "iphone",
1010 "size" : "20x20",
11+ "idiom" : "iphone",
12+ "filename" : "60.png",
1113 "scale" : "3x"
1214 },
1315 {
14− "idiom" : "iphone",
1516 "size" : "29x29",
17+ "idiom" : "iphone",
18+ "filename" : "58-1.png",
1619 "scale" : "2x"
1720 },
1821 {
19− "idiom" : "iphone",
2022 "size" : "29x29",
23+ "idiom" : "iphone",
24+ "filename" : "87.png",
2125 "scale" : "3x"
2226 },
2327 {
24− "idiom" : "iphone",
2528 "size" : "40x40",
29+ "idiom" : "iphone",
30+ "filename" : "80.png",
2631 "scale" : "2x"
2732 },
2833 {
29− "idiom" : "iphone",
3034 "size" : "40x40",
35+ "idiom" : "iphone",
36+ "filename" : "120.png",
3137 "scale" : "3x"
3238 },
3339 {
34− "idiom" : "iphone",
3540 "size" : "60x60",
41+ "idiom" : "iphone",
42+ "filename" : "120-1.png",
3643 "scale" : "2x"
3744 },
3845 {
39− "idiom" : "iphone",
4046 "size" : "60x60",
47+ "idiom" : "iphone",
48+ "filename" : "180.png",
4149 "scale" : "3x"
4250 },
4351 {
44− "idiom" : "ipad",
4552 "size" : "20x20",
53+ "idiom" : "ipad",
54+ "filename" : "20.png",
4655 "scale" : "1x"
4756 },
4857 {
49− "idiom" : "ipad",
5058 "size" : "20x20",
59+ "idiom" : "ipad",
60+ "filename" : "40.png",
5161 "scale" : "2x"
5262 },
5363 {
54− "idiom" : "ipad",
5564 "size" : "29x29",
65+ "idiom" : "ipad",
66+ "filename" : "29.png",
5667 "scale" : "1x"
5768 },
5869 {
59− "idiom" : "ipad",
6070 "size" : "29x29",
71+ "idiom" : "ipad",
72+ "filename" : "58.png",
6173 "scale" : "2x"
6274 },
6375 {
64− "idiom" : "ipad",
6576 "size" : "40x40",
77+ "idiom" : "ipad",
78+ "filename" : "40-2.png",
6679 "scale" : "1x"
6780 },
6881 {
69− "idiom" : "ipad",
7082 "size" : "40x40",
83+ "idiom" : "ipad",
84+ "filename" : "80-1.png",
7185 "scale" : "2x"
7286 },
7387 {
74− "idiom" : "ipad",
7588 "size" : "76x76",
89+ "idiom" : "ipad",
90+ "filename" : "76.png",
7691 "scale" : "1x"
7792 },
7893 {
79− "idiom" : "ipad",
8094 "size" : "76x76",
95+ "idiom" : "ipad",
96+ "filename" : "152.png",
8197 "scale" : "2x"
8298 },
8399 {
84− "idiom" : "ipad",
85100 "size" : "83.5x83.5",
101+ "idiom" : "ipad",
102+ "filename" : "167.png",
86103 "scale" : "2x"
87104 },
88105 {
89− "idiom" : "ios-marketing",
90106 "size" : "1024x1024",
107+ "idiom" : "ios-marketing",
108+ "filename" : "1024.png",
91109 "scale" : "1x"
92110 }
93111 ],
Hookah Tau/Supporting Files/Assets.xcassets/admin.imageset/Contents.jsonnew file+12
@@ -0,0 +1,12 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "admin.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ }
12+}
Hookah Tau/Supporting Files/Assets.xcassets/admin.imageset/admin.pdfnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/blacklist.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "blacklist.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ },
12+ "properties" : {
13+ "preserves-vector-representation" : true
14+ }
15+}
Hookah Tau/Supporting Files/Assets.xcassets/blacklist.imageset/blacklist.pdfnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/peoplee.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "peoplee.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ },
12+ "properties" : {
13+ "template-rendering-intent" : "template"
14+ }
15+}
Hookah Tau/Supporting Files/Assets.xcassets/peoplee.imageset/peoplee.pdfnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/person.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "person.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ },
12+ "properties" : {
13+ "template-rendering-intent" : "template"
14+ }
15+}
Hookah Tau/Supporting Files/Assets.xcassets/person.imageset/person.pdfnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/stack.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "Light-S.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ },
12+ "properties" : {
13+ "template-rendering-intent" : "template"
14+ }
15+}
Hookah Tau/Supporting Files/Assets.xcassets/stack.imageset/Light-S.pdfnew file

Binary file not shown.

Hookah Tau/Supporting Files/Assets.xcassets/stack2.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "stack2.pdf"
6+ }
7+ ],
8+ "info" : {
9+ "version" : 1,
10+ "author" : "xcode"
11+ },
12+ "properties" : {
13+ "preserves-vector-representation" : true
14+ }
15+}
Hookah Tau/Supporting Files/Assets.xcassets/stack2.imageset/stack2.pdfnew file

Binary file not shown.