git.iko.soy/dashared/hookah-tau
on develop help sign in
Merge pull request #20 from dashared/admin/coordinators

Admin/coordinators - админка

commitfb6f9c49f1c4dc6f8ce9566b7582a69f26543e9b authorDarya Rednikina <31587869+dashared@users.noreply.github.com> dateSat 4 Jan 2020, 15:03 (7 years ago) parent1445be5c591547
77 files changed+3123−62
Hookah Tau.xcodeproj/project.pbxproj+270−4 Hookah Tau/API/Authorization api/AuthorizationService.swift+5 Hookah Tau/API/ClientsService/ClientsService.swift+66 Hookah Tau/API/ClientsService/Resolvers/ClientReservationsResolver.swift+34 Hookah Tau/API/ClientsService/Resolvers/ClientsListResolver.swift+28 Hookah Tau/API/Reservations api/ReservationsService.swift+55−1 Hookah Tau/API/Reservations api/Resolvers/Admin/AChangeReservationResolver.swift+34 Hookah Tau/API/Reservations api/Resolvers/Admin/AReservationsResolver.swift+35 Hookah Tau/API/Reservations api/Resolvers/ReservationsAroundResolver.swift+35 Hookah Tau/API/Settings/Resolvers/AddOtherAdminResolver.swift+35 Hookah Tau/API/Settings/SettingsService.swift+34 Hookah Tau/Controllers/Admin/Clients/Cells/ClientTableViewCell.swift+44 Hookah Tau/Controllers/Admin/Clients/ClientList.storyboard+149 Hookah Tau/Controllers/Admin/Clients/ClientsCoordinator.swift+36 Hookah Tau/Controllers/Admin/Clients/ClientsViewController.swift+72 Hookah Tau/Controllers/Admin/Clients/Profile/ClientProfileCoordinator.swift+29 Hookah Tau/Controllers/Admin/Clients/Profile/ProfileTableViewController.swift+98 Hookah Tau/Controllers/Admin/Reservations/AdminAddressCoordinator.swift+35 Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingCoordinator.swift+29 Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingViewController.swift+139 Hookah Tau/Controllers/Admin/Reservations/AdminReservations.storyboard+68 Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsController.swift+145 Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsCoordinator.swift+38 Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.swift+84 Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.xib+93 Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.swift+364 Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.xib+208 Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.swift+22 Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.xib+53 Hookah Tau/Controllers/Admin/Tab bar/Admin.storyboard+79 Hookah Tau/Controllers/Admin/Tab bar/AdminTabbarController.swift+48 Hookah Tau/Controllers/Admin/Tab bar/AdminTabbarCoordinator.swift+73 Hookah Tau/Controllers/ApplicationCoordinator.swift+8−1 Hookah Tau/Controllers/BaseViewController.swift+1−1 Hookah Tau/Controllers/Client/Profile/ProfileChangeCoordinator.swift+6−4 Hookah Tau/Controllers/Client/Profile/ProfileChangeViewController.swift+22−3 Hookah Tau/Controllers/Client/Profile/Views/ChangeView.swift+32−1 Hookah Tau/Controllers/Client/Profile/Views/ChangeView.xib+1−1 Hookah Tau/Controllers/Client/Profile/Views/ProfileView.xib+5−5 Hookah Tau/Controllers/Client/Reservations/Active Reservations/ReservationsViewController.swift−2 Hookah Tau/Controllers/Client/Reservations/Adress of reservation/AddresViewController.swift+2−2 Hookah Tau/Controllers/Client/Reservations/Adress of reservation/AddressCoordinator.swift+11−1 Hookah Tau/Controllers/Client/Reservations/Cells/Reservation/ReservationCell.swift+2 Hookah Tau/Controllers/Client/Reservations/Maps/MapView.swift+1 Hookah Tau/Controllers/Client/Reservations/Steps/2/SecondStepReservationViewController.swift+4−4 Hookah Tau/Controllers/Client/Reservations/Steps/change /ChangeReservationViewController.swift+1 Hookah Tau/Controllers/Client/Tab bar/ClientTabbarController.swift+6−6 Hookah Tau/Controllers/Client/Tab bar/ClientTabbarCoordinator.swift+3−3 Hookah Tau/Controllers/Picker view/IntervalPickerView.swift+265 Hookah Tau/Controllers/Picker view/IntervalPickerView.xib+84 Hookah Tau/DB/DataStorage.swift+15 Hookah Tau/Extensions+String.swift+36 Hookah Tau/Info.plist+5−3 Hookah Tau/Models/Reservation.swift+15 Hookah Tau/Models/User.swift+9 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/arrow.imageset/Contents.json+15 Hookah Tau/Supporting Files/Assets.xcassets/arrow.imageset/arrow.pdf Hookah Tau/Views/Phone/PhoneView.swift+1−2
Hookah Tau.xcodeproj/project.pbxproj+270−4
@@ -28,6 +28,10 @@
2828 123615CF235510BC00D1122A /* ProfileView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 123615CE235510BC00D1122A /* ProfileView.xib */; };
2929 123615D12355141600D1122A /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123615D02355141600D1122A /* ProfileView.swift */; };
3030 12387337234BB98700D1F1AB /* CodeView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12387336234BB98700D1F1AB /* CodeView.xib */; };
31+ 123D25F0239E3E11002FA32B /* AdminAddressCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123D25EF239E3E11002FA32B /* AdminAddressCoordinator.swift */; };
32+ 123D25F2239E3E44002FA32B /* ClientsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123D25F1239E3E44002FA32B /* ClientsCoordinator.swift */; };
33+ 123D25F4239E3FF5002FA32B /* ClientsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123D25F3239E3FF5002FA32B /* ClientsViewController.swift */; };
34+ 123D25F7239E5839002FA32B /* ClientTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123D25F6239E5839002FA32B /* ClientTableViewCell.swift */; };
3135 1246B717235CD186004CBFA1 /* NoReservationsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1246B716235CD186004CBFA1 /* NoReservationsView.xib */; };
3236 1246B719235CD3AD004CBFA1 /* NoReservationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1246B718235CD3AD004CBFA1 /* NoReservationsView.swift */; };
3337 1246B71C235CDF3A004CBFA1 /* ReservationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1246B71B235CDF3A004CBFA1 /* ReservationCell.swift */; };
@@ -53,6 +57,24 @@
5357 12A075FD237357E900D749BB /* FirstStepReservationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A075FC237357E900D749BB /* FirstStepReservationCoordinator.swift */; };
5458 12A075FF237358F200D749BB /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A075FE237358F200D749BB /* MapView.swift */; };
5559 12A07601237359BB00D749BB /* MapView1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A07600237359BB00D749BB /* MapView1.swift */; };
60+ 12B3A79823ABADB9009E2FA6 /* AdminReservationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A79723ABADB9009E2FA6 /* AdminReservationView.xib */; };
61+ 12B3A79A23ABB4E2009E2FA6 /* IntervalPickerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A79923ABB4E2009E2FA6 /* IntervalPickerView.xib */; };
62+ 12B3A79C23ABB8ED009E2FA6 /* IntervalPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A79B23ABB8ED009E2FA6 /* IntervalPickerView.swift */; };
63+ 12B3A79F23ABCA21009E2FA6 /* AdminReservationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A79E23ABCA21009E2FA6 /* AdminReservationView.swift */; };
64+ 12B3A7A323ABD594009E2FA6 /* AdminReservationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7A223ABD594009E2FA6 /* AdminReservationsController.swift */; };
65+ 12B3A7A523ABDA25009E2FA6 /* AdminReservationsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7A423ABDA25009E2FA6 /* AdminReservationsCoordinator.swift */; };
66+ 12B3A7A823ABDDB1009E2FA6 /* AdminReservationCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7A723ABDDB1009E2FA6 /* AdminReservationCellView.swift */; };
67+ 12B3A7AA23ABDDC1009E2FA6 /* AdminReservations.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A7A923ABDDC1009E2FA6 /* AdminReservations.storyboard */; };
68+ 12B3A7AD23ABEABF009E2FA6 /* AReservationsResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7AC23ABEABF009E2FA6 /* AReservationsResolver.swift */; };
69+ 12B3A7AF23AC1872009E2FA6 /* AdminNoReservationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7AE23AC1872009E2FA6 /* AdminNoReservationsView.swift */; };
70+ 12B3A7B123AC1885009E2FA6 /* AdminNoReservationsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A7B023AC1885009E2FA6 /* AdminNoReservationsView.xib */; };
71+ 12B3A7B423AC1D09009E2FA6 /* AdminReservationCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12B3A7B323AC1D09009E2FA6 /* AdminReservationCellView.xib */; };
72+ 12B3A7B723ACCD07009E2FA6 /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */; };
73+ 12B3A7B923ACCD83009E2FA6 /* AddOtherAdminResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */; };
74+ 12B3A7BC23ACFD75009E2FA6 /* ReservationsAroundResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */; };
75+ 12B3A7BE23AD0049009E2FA6 /* AdminReservationEditingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */; };
76+ 12B3A7C023AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */; };
77+ 12B3A7C423AD2FCD009E2FA6 /* AChangeReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */; };
5678 12B4447C238EF25C000B6B16 /* CreateReservationResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */; };
5779 12B6A60F235B857A00898B31 /* ProfileChangeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6A60E235B857A00898B31 /* ProfileChangeCoordinator.swift */; };
5880 12B6A611235B8EC500898B31 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B6A610235B8EC500898B31 /* User.swift */; };
@@ -63,6 +85,10 @@
6385 12BD95B2237EC22B009614D8 /* FirstStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BD95B1237EC22B009614D8 /* FirstStepView.swift */; };
6486 12BD95B6237ED8AC009614D8 /* TimePointView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12BD95B5237ED8AB009614D8 /* TimePointView.xib */; };
6587 12BD95B8237EDB01009614D8 /* TimePointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BD95B7237EDB01009614D8 /* TimePointView.swift */; };
88+ 12BE4B332399A52E00FC974B /* AdminTabbarCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BE4B322399A52E00FC974B /* AdminTabbarCoordinator.swift */; };
89+ 12BE4B352399A60A00FC974B /* AdminTabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12BE4B342399A60A00FC974B /* AdminTabbarController.swift */; };
90+ 12BE4B372399A98200FC974B /* Admin.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12BE4B362399A98200FC974B /* Admin.storyboard */; };
91+ 12C0B4BB239E5A3A00562BA3 /* ClientList.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12C0B4BA239E5A3A00562BA3 /* ClientList.storyboard */; };
6692 12C72EDF23537D1B00AD7004 /* ProfileCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C72EDE23537D1B00AD7004 /* ProfileCoordinator.swift */; };
6793 12C72EE1235380D400AD7004 /* ProfileClientViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C72EE0235380D400AD7004 /* ProfileClientViewController.swift */; };
6894 12C72EE3235380EB00AD7004 /* ReservationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12C72EE2235380EB00AD7004 /* ReservationsViewController.swift */; };
@@ -70,6 +96,12 @@
7096 12D8FA2F237F00E200305F57 /* DateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D8FA2E237F00E200305F57 /* DateView.swift */; };
7197 12D8FA31237F08C700305F57 /* AllReservations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12D8FA30237F08C700305F57 /* AllReservations.swift */; };
7298 12DBEEB223539F2100AF1BB6 /* Client.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 12DBEEB123539F2100AF1BB6 /* Client.storyboard */; };
99+ 12E35071239FA8CA004BB188 /* Extensions+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E35070239FA8CA004BB188 /* Extensions+String.swift */; };
100+ 12E35074239FAF52004BB188 /* ClientsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E35073239FAF52004BB188 /* ClientsService.swift */; };
101+ 12E35078239FAFA4004BB188 /* ClientsListResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E35077239FAFA4004BB188 /* ClientsListResolver.swift */; };
102+ 12E3507B239FCFB2004BB188 /* ProfileTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E3507A239FCFB2004BB188 /* ProfileTableViewController.swift */; };
103+ 12E3507D239FD1F3004BB188 /* ClientReservationsResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E3507C239FD1F3004BB188 /* ClientReservationsResolver.swift */; };
104+ 12E3507F239FDFC2004BB188 /* ClientProfileCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12E3507E239FDFC2004BB188 /* ClientProfileCoordinator.swift */; };
73105 12EA99462362B674005D9FE9 /* AddressCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12EA99452362B674005D9FE9 /* AddressCoordinator.swift */; };
74106 12EA99482362B684005D9FE9 /* AddresViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12EA99472362B684005D9FE9 /* AddresViewController.swift */; };
75107 12EA994C2362F8EF005D9FE9 /* AddressView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 12EA994B2362F8EF005D9FE9 /* AddressView.xib */; };
@@ -151,6 +183,10 @@
151183 123615CE235510BC00D1122A /* ProfileView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfileView.xib; sourceTree = "<group>"; };
152184 123615D02355141600D1122A /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
153185 12387336234BB98700D1F1AB /* CodeView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CodeView.xib; sourceTree = "<group>"; };
186+ 123D25EF239E3E11002FA32B /* AdminAddressCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminAddressCoordinator.swift; sourceTree = "<group>"; };
187+ 123D25F1239E3E44002FA32B /* ClientsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientsCoordinator.swift; sourceTree = "<group>"; };
188+ 123D25F3239E3FF5002FA32B /* ClientsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientsViewController.swift; sourceTree = "<group>"; };
189+ 123D25F6239E5839002FA32B /* ClientTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTableViewCell.swift; sourceTree = "<group>"; };
154190 1246B716235CD186004CBFA1 /* NoReservationsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NoReservationsView.xib; sourceTree = "<group>"; };
155191 1246B718235CD3AD004CBFA1 /* NoReservationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoReservationsView.swift; sourceTree = "<group>"; };
156192 1246B71B235CDF3A004CBFA1 /* ReservationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationCell.swift; sourceTree = "<group>"; };
@@ -175,6 +211,24 @@
175211 12A075FC237357E900D749BB /* FirstStepReservationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstStepReservationCoordinator.swift; sourceTree = "<group>"; };
176212 12A075FE237358F200D749BB /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
177213 12A07600237359BB00D749BB /* MapView1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView1.swift; sourceTree = "<group>"; };
214+ 12B3A79723ABADB9009E2FA6 /* AdminReservationView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdminReservationView.xib; sourceTree = "<group>"; };
215+ 12B3A79923ABB4E2009E2FA6 /* IntervalPickerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = IntervalPickerView.xib; sourceTree = "<group>"; };
216+ 12B3A79B23ABB8ED009E2FA6 /* IntervalPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntervalPickerView.swift; sourceTree = "<group>"; };
217+ 12B3A79E23ABCA21009E2FA6 /* AdminReservationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationView.swift; sourceTree = "<group>"; };
218+ 12B3A7A223ABD594009E2FA6 /* AdminReservationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationsController.swift; sourceTree = "<group>"; };
219+ 12B3A7A423ABDA25009E2FA6 /* AdminReservationsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationsCoordinator.swift; sourceTree = "<group>"; };
220+ 12B3A7A723ABDDB1009E2FA6 /* AdminReservationCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationCellView.swift; sourceTree = "<group>"; };
221+ 12B3A7A923ABDDC1009E2FA6 /* AdminReservations.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = AdminReservations.storyboard; sourceTree = "<group>"; };
222+ 12B3A7AC23ABEABF009E2FA6 /* AReservationsResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AReservationsResolver.swift; sourceTree = "<group>"; };
223+ 12B3A7AE23AC1872009E2FA6 /* AdminNoReservationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminNoReservationsView.swift; sourceTree = "<group>"; };
224+ 12B3A7B023AC1885009E2FA6 /* AdminNoReservationsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdminNoReservationsView.xib; sourceTree = "<group>"; };
225+ 12B3A7B323AC1D09009E2FA6 /* AdminReservationCellView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AdminReservationCellView.xib; sourceTree = "<group>"; };
226+ 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = "<group>"; };
227+ 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOtherAdminResolver.swift; sourceTree = "<group>"; };
228+ 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationsAroundResolver.swift; sourceTree = "<group>"; };
229+ 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationEditingViewController.swift; sourceTree = "<group>"; };
230+ 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReservationEditingCoordinator.swift; sourceTree = "<group>"; };
231+ 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AChangeReservationResolver.swift; sourceTree = "<group>"; };
178232 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateReservationResolver.swift; sourceTree = "<group>"; };
179233 12B6A60E235B857A00898B31 /* ProfileChangeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileChangeCoordinator.swift; sourceTree = "<group>"; };
180234 12B6A610235B8EC500898B31 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
@@ -185,6 +239,10 @@
185239 12BD95B1237EC22B009614D8 /* FirstStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstStepView.swift; sourceTree = "<group>"; };
186240 12BD95B5237ED8AB009614D8 /* TimePointView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TimePointView.xib; sourceTree = "<group>"; };
187241 12BD95B7237EDB01009614D8 /* TimePointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePointView.swift; sourceTree = "<group>"; };
242+ 12BE4B322399A52E00FC974B /* AdminTabbarCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTabbarCoordinator.swift; sourceTree = "<group>"; };
243+ 12BE4B342399A60A00FC974B /* AdminTabbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminTabbarController.swift; sourceTree = "<group>"; };
244+ 12BE4B362399A98200FC974B /* Admin.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Admin.storyboard; sourceTree = "<group>"; };
245+ 12C0B4BA239E5A3A00562BA3 /* ClientList.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ClientList.storyboard; sourceTree = "<group>"; };
188246 12C72EDE23537D1B00AD7004 /* ProfileCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileCoordinator.swift; sourceTree = "<group>"; };
189247 12C72EE0235380D400AD7004 /* ProfileClientViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileClientViewController.swift; sourceTree = "<group>"; };
190248 12C72EE2235380EB00AD7004 /* ReservationsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReservationsViewController.swift; sourceTree = "<group>"; };
@@ -192,6 +250,12 @@
192250 12D8FA2E237F00E200305F57 /* DateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateView.swift; sourceTree = "<group>"; };
193251 12D8FA30237F08C700305F57 /* AllReservations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllReservations.swift; sourceTree = "<group>"; };
194252 12DBEEB123539F2100AF1BB6 /* Client.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Client.storyboard; sourceTree = "<group>"; };
253+ 12E35070239FA8CA004BB188 /* Extensions+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extensions+String.swift"; sourceTree = "<group>"; };
254+ 12E35073239FAF52004BB188 /* ClientsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientsService.swift; sourceTree = "<group>"; };
255+ 12E35077239FAFA4004BB188 /* ClientsListResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientsListResolver.swift; sourceTree = "<group>"; };
256+ 12E3507A239FCFB2004BB188 /* ProfileTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTableViewController.swift; sourceTree = "<group>"; };
257+ 12E3507C239FD1F3004BB188 /* ClientReservationsResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientReservationsResolver.swift; sourceTree = "<group>"; };
258+ 12E3507E239FDFC2004BB188 /* ClientProfileCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProfileCoordinator.swift; sourceTree = "<group>"; };
195259 12EA99452362B674005D9FE9 /* AddressCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCoordinator.swift; sourceTree = "<group>"; };
196260 12EA99472362B684005D9FE9 /* AddresViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddresViewController.swift; sourceTree = "<group>"; };
197261 12EA994B2362F8EF005D9FE9 /* AddressView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddressView.xib; sourceTree = "<group>"; };
@@ -388,6 +452,45 @@
388452 path = Views;
389453 sourceTree = "<group>";
390454 };
455+ 123D25EC239E3D47002FA32B /* Profile */ = {
456+ isa = PBXGroup;
457+ children = (
458+ );
459+ path = Profile;
460+ sourceTree = "<group>";
461+ };
462+ 123D25ED239E3D50002FA32B /* Reservations */ = {
463+ isa = PBXGroup;
464+ children = (
465+ 12B3A79623ABAD9A009E2FA6 /* Views */,
466+ 12B3A7C123AD0751009E2FA6 /* Editing */,
467+ 12B3A7C223AD0762009E2FA6 /* List of reservations */,
468+ 123D25EF239E3E11002FA32B /* AdminAddressCoordinator.swift */,
469+ 12B3A7A923ABDDC1009E2FA6 /* AdminReservations.storyboard */,
470+ );
471+ path = Reservations;
472+ sourceTree = "<group>";
473+ };
474+ 123D25EE239E3D61002FA32B /* Clients */ = {
475+ isa = PBXGroup;
476+ children = (
477+ 12E35079239FCF91004BB188 /* Profile */,
478+ 123D25F5239E57D6002FA32B /* Cells */,
479+ 123D25F1239E3E44002FA32B /* ClientsCoordinator.swift */,
480+ 123D25F3239E3FF5002FA32B /* ClientsViewController.swift */,
481+ 12C0B4BA239E5A3A00562BA3 /* ClientList.storyboard */,
482+ );
483+ path = Clients;
484+ sourceTree = "<group>";
485+ };
486+ 123D25F5239E57D6002FA32B /* Cells */ = {
487+ isa = PBXGroup;
488+ children = (
489+ 123D25F6239E5839002FA32B /* ClientTableViewCell.swift */,
490+ );
491+ path = Cells;
492+ sourceTree = "<group>";
493+ };
391494 1246B71A235CD4C5004CBFA1 /* Views */ = {
392495 isa = PBXGroup;
393496 children = (
@@ -418,10 +521,12 @@
418521 124D00822372233000AE76B9 /* Resolvers */ = {
419522 isa = PBXGroup;
420523 children = (
524+ 12B3A7AB23ABEAAB009E2FA6 /* Admin */,
421525 124D007E237222B200AE76B9 /* ReservationsResolver.swift */,
422526 12B4447B238EF25C000B6B16 /* CreateReservationResolver.swift */,
423527 1212048B23914CFD00CAF162 /* DeleteReservationResolver.swift */,
424528 1212048D23915A0A00CAF162 /* ChangeReservationResolver.swift */,
529+ 12B3A7BB23ACFD75009E2FA6 /* ReservationsAroundResolver.swift */,
425530 );
426531 path = Resolvers;
427532 sourceTree = "<group>";
@@ -438,11 +543,13 @@
438543 12838EB923679AEB00CEF108 /* API */ = {
439544 isa = PBXGroup;
440545 children = (
546+ 12B3A7B523ACCCD0009E2FA6 /* Settings */,
441547 12838EA8236764A600CEF108 /* APIClient.swift */,
442548 12838EB3236779C700CEF108 /* ApiResolver.swift */,
443549 12838EAF236777D400CEF108 /* ServerError.swift */,
444550 12838EB523677FB900CEF108 /* ApiRequest.swift */,
445551 12838EB72367834900CEF108 /* MyCodable.swift */,
552+ 12E35072239FAF41004BB188 /* ClientsService */,
446553 12230AF223900BB6004A01E5 /* Establishments api */,
447554 124D007D2372229500AE76B9 /* Reservations api */,
448555 12118B9A2368187B006BAEE3 /* Authorization api */,
@@ -450,6 +557,88 @@
450557 path = API;
451558 sourceTree = "<group>";
452559 };
560+ 12B3A79623ABAD9A009E2FA6 /* Views */ = {
561+ isa = PBXGroup;
562+ children = (
563+ 12B3A7B223AC1A1F009E2FA6 /* No reserv */,
564+ 12B3A79723ABADB9009E2FA6 /* AdminReservationView.xib */,
565+ 12B3A79E23ABCA21009E2FA6 /* AdminReservationView.swift */,
566+ );
567+ path = Views;
568+ sourceTree = "<group>";
569+ };
570+ 12B3A79D23ABB8F4009E2FA6 /* Picker view */ = {
571+ isa = PBXGroup;
572+ children = (
573+ 12B3A79923ABB4E2009E2FA6 /* IntervalPickerView.xib */,
574+ 12B3A79B23ABB8ED009E2FA6 /* IntervalPickerView.swift */,
575+ );
576+ path = "Picker view";
577+ sourceTree = "<group>";
578+ };
579+ 12B3A7A623ABDD92009E2FA6 /* Cell */ = {
580+ isa = PBXGroup;
581+ children = (
582+ 12B3A7A723ABDDB1009E2FA6 /* AdminReservationCellView.swift */,
583+ 12B3A7B323AC1D09009E2FA6 /* AdminReservationCellView.xib */,
584+ );
585+ path = Cell;
586+ sourceTree = "<group>";
587+ };
588+ 12B3A7AB23ABEAAB009E2FA6 /* Admin */ = {
589+ isa = PBXGroup;
590+ children = (
591+ 12B3A7AC23ABEABF009E2FA6 /* AReservationsResolver.swift */,
592+ 12B3A7C323AD2FCD009E2FA6 /* AChangeReservationResolver.swift */,
593+ );
594+ path = Admin;
595+ sourceTree = "<group>";
596+ };
597+ 12B3A7B223AC1A1F009E2FA6 /* No reserv */ = {
598+ isa = PBXGroup;
599+ children = (
600+ 12B3A7AE23AC1872009E2FA6 /* AdminNoReservationsView.swift */,
601+ 12B3A7B023AC1885009E2FA6 /* AdminNoReservationsView.xib */,
602+ );
603+ path = "No reserv";
604+ sourceTree = "<group>";
605+ };
606+ 12B3A7B523ACCCD0009E2FA6 /* Settings */ = {
607+ isa = PBXGroup;
608+ children = (
609+ 12B3A7B623ACCD07009E2FA6 /* SettingsService.swift */,
610+ 12B3A7BA23ACCD88009E2FA6 /* Resolvers */,
611+ );
612+ path = Settings;
613+ sourceTree = "<group>";
614+ };
615+ 12B3A7BA23ACCD88009E2FA6 /* Resolvers */ = {
616+ isa = PBXGroup;
617+ children = (
618+ 12B3A7B823ACCD83009E2FA6 /* AddOtherAdminResolver.swift */,
619+ );
620+ path = Resolvers;
621+ sourceTree = "<group>";
622+ };
623+ 12B3A7C123AD0751009E2FA6 /* Editing */ = {
624+ isa = PBXGroup;
625+ children = (
626+ 12B3A7BF23AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift */,
627+ 12B3A7BD23AD0048009E2FA6 /* AdminReservationEditingViewController.swift */,
628+ );
629+ name = Editing;
630+ sourceTree = "<group>";
631+ };
632+ 12B3A7C223AD0762009E2FA6 /* List of reservations */ = {
633+ isa = PBXGroup;
634+ children = (
635+ 12B3A7A623ABDD92009E2FA6 /* Cell */,
636+ 12B3A7A223ABD594009E2FA6 /* AdminReservationsController.swift */,
637+ 12B3A7A423ABDA25009E2FA6 /* AdminReservationsCoordinator.swift */,
638+ );
639+ path = "List of reservations";
640+ sourceTree = "<group>";
641+ };
453642 12BD95AE237EBE52009614D8 /* Maps */ = {
454643 isa = PBXGroup;
455644 children = (
@@ -483,6 +672,16 @@
483672 path = 1;
484673 sourceTree = "<group>";
485674 };
675+ 12BE4B312399A51500FC974B /* Tab bar */ = {
676+ isa = PBXGroup;
677+ children = (
678+ 12BE4B322399A52E00FC974B /* AdminTabbarCoordinator.swift */,
679+ 12BE4B342399A60A00FC974B /* AdminTabbarController.swift */,
680+ 12BE4B362399A98200FC974B /* Admin.storyboard */,
681+ );
682+ path = "Tab bar";
683+ sourceTree = "<group>";
684+ };
486685 12C72EDB235367CF00AD7004 /* Tab bar */ = {
487686 isa = PBXGroup;
488687 children = (
@@ -493,6 +692,33 @@
493692 path = "Tab bar";
494693 sourceTree = "<group>";
495694 };
695+ 12E35072239FAF41004BB188 /* ClientsService */ = {
696+ isa = PBXGroup;
697+ children = (
698+ 12E35073239FAF52004BB188 /* ClientsService.swift */,
699+ 12E35075239FAF8F004BB188 /* Resolvers */,
700+ );
701+ path = ClientsService;
702+ sourceTree = "<group>";
703+ };
704+ 12E35075239FAF8F004BB188 /* Resolvers */ = {
705+ isa = PBXGroup;
706+ children = (
707+ 12E35077239FAFA4004BB188 /* ClientsListResolver.swift */,
708+ 12E3507C239FD1F3004BB188 /* ClientReservationsResolver.swift */,
709+ );
710+ path = Resolvers;
711+ sourceTree = "<group>";
712+ };
713+ 12E35079239FCF91004BB188 /* Profile */ = {
714+ isa = PBXGroup;
715+ children = (
716+ 12E3507A239FCFB2004BB188 /* ProfileTableViewController.swift */,
717+ 12E3507E239FDFC2004BB188 /* ClientProfileCoordinator.swift */,
718+ );
719+ path = Profile;
720+ sourceTree = "<group>";
721+ };
496722 12EA99492362B9F5005D9FE9 /* Adress of reservation */ = {
497723 isa = PBXGroup;
498724 children = (
@@ -566,6 +792,7 @@
566792 F3FC7DFC2315C4710080D851 /* BaseCoordinator.swift */,
567793 F3FC7DFA2315C25D0080D851 /* ApplicationCoordinator.swift */,
568794 12B6A612235B9B4300898B31 /* BaseViewController.swift */,
795+ 12B3A79D23ABB8F4009E2FA6 /* Picker view */,
569796 F30369BB2336845F00A8EA84 /* Admin */,
570797 F30369BA2336845400A8EA84 /* Client */,
571798 F30369B82336842600A8EA84 /* Authentication */,
@@ -609,6 +836,10 @@
609836 F30369BB2336845F00A8EA84 /* Admin */ = {
610837 isa = PBXGroup;
611838 children = (
839+ 123D25ED239E3D50002FA32B /* Reservations */,
840+ 123D25EE239E3D61002FA32B /* Clients */,
841+ 123D25EC239E3D47002FA32B /* Profile */,
842+ 12BE4B312399A51500FC974B /* Tab bar */,
612843 );
613844 path = Admin;
614845 sourceTree = "<group>";
@@ -718,6 +949,7 @@
718949 F31E20C62348DDB900BEA215 /* Extensions+UIView.swift */,
719950 12050568238048CE00801618 /* Extensions+Date.swift */,
720951 1205056A2380588C00801618 /* Extenstions+IBDesignable.swift */,
952+ 12E35070239FA8CA004BB188 /* Extensions+String.swift */,
721953 125AE5202395753400F847BD /* HFunc.swift */,
722954 12838EB923679AEB00CEF108 /* API */,
723955 F30369BC2336861100A8EA84 /* Models */,
@@ -859,8 +1091,13 @@
8591091 buildActionMask = 2147483647;
8601092 files = (
8611093 F3199E5B233FE08200F3C17A /* PhoneView.xib in Resources */,
1094+ 12B3A7AA23ABDDC1009E2FA6 /* AdminReservations.storyboard in Resources */,
1095+ 12B3A7B423AC1D09009E2FA6 /* AdminReservationCellView.xib in Resources */,
1096+ 12C0B4BB239E5A3A00562BA3 /* ClientList.storyboard in Resources */,
1097+ 12BE4B372399A98200FC974B /* Admin.storyboard in Resources */,
8621098 12DBEEB223539F2100AF1BB6 /* Client.storyboard in Resources */,
8631099 12D8FA2D237F003800305F57 /* DateView.xib in Resources */,
1100+ 12B3A79A23ABB4E2009E2FA6 /* IntervalPickerView.xib in Resources */,
8641101 12387337234BB98700D1F1AB /* CodeView.xib in Resources */,
8651102 12B6A615235BB36C00898B31 /* ChangeView.xib in Resources */,
8661103 F3851B2C2340A280009EE0B6 /* TitleTextView.xib in Resources */,
@@ -874,6 +1111,8 @@
8741111 12BD95B0237EBEDF009614D8 /* FirstStepView.xib in Resources */,
8751112 F30369B5233679EA00A8EA84 /* LaunchScreen.storyboard in Resources */,
8761113 1246B717235CD186004CBFA1 /* NoReservationsView.xib in Resources */,
1114+ 12B3A7B123AC1885009E2FA6 /* AdminNoReservationsView.xib in Resources */,
1115+ 12B3A79823ABADB9009E2FA6 /* AdminReservationView.xib in Resources */,
8771116 F3FC7DD42315BE630080D851 /* Assets.xcassets in Resources */,
8781117 12EA994C2362F8EF005D9FE9 /* AddressView.xib in Resources */,
8791118 F3EBE2452340FDA7000FEE1A /* NameTextView.xib in Resources */,
@@ -907,6 +1146,8 @@
9071146 12230AF723900D44004A01E5 /* EstablishmentResolver.swift in Sources */,
9081147 1246B719235CD3AD004CBFA1 /* NoReservationsView.swift in Sources */,
9091148 F3895F4A233D323700FA6A37 /* CodeCoordinator.swift in Sources */,
1149+ 12B3A7B923ACCD83009E2FA6 /* AddOtherAdminResolver.swift in Sources */,
1150+ 12E3507F239FDFC2004BB188 /* ClientProfileCoordinator.swift in Sources */,
9101151 12838EB4236779C700CEF108 /* ApiResolver.swift in Sources */,
9111152 12838EA9236764A600CEF108 /* APIClient.swift in Sources */,
9121153 123615D12355141600D1122A /* ProfileView.swift in Sources */,
@@ -915,13 +1156,20 @@
9151156 125AE51F2395585300F847BD /* ChangeReservationViewController.swift in Sources */,
9161157 12EA99462362B674005D9FE9 /* AddressCoordinator.swift in Sources */,
9171158 12B6A613235B9B4300898B31 /* BaseViewController.swift in Sources */,
1159+ 12BE4B332399A52E00FC974B /* AdminTabbarCoordinator.swift in Sources */,
9181160 12230AFA23901029004A01E5 /* DataStorage.swift in Sources */,
1161+ 12B3A7A823ABDDB1009E2FA6 /* AdminReservationCellView.swift in Sources */,
9191162 F3FC7DFF2315C8D70080D851 /* NameCoordinator.swift in Sources */,
1163+ 123D25F7239E5839002FA32B /* ClientTableViewCell.swift in Sources */,
9201164 120BFC7C23891965001EBE89 /* ThirdStepView.swift in Sources */,
1165+ 123D25F0239E3E11002FA32B /* AdminAddressCoordinator.swift in Sources */,
9211166 F3FC7DCF2315BE610080D851 /* NameViewController.swift in Sources */,
1167+ 12B3A79F23ABCA21009E2FA6 /* AdminReservationView.swift in Sources */,
9221168 12838EB82367834900CEF108 /* MyCodable.swift in Sources */,
9231169 F3DC5A05233E89D500B5E751 /* Button.swift in Sources */,
9241170 F3FC7DFB2315C25D0080D851 /* ApplicationCoordinator.swift in Sources */,
1171+ 12B3A7BE23AD0049009E2FA6 /* AdminReservationEditingViewController.swift in Sources */,
1172+ 12BE4B352399A60A00FC974B /* AdminTabbarController.swift in Sources */,
9251173 12FC403A2382CB6F00AB593B /* TableButton.swift in Sources */,
9261174 12EB698123661CB300E4D1CE /* AddressCell.swift in Sources */,
9271175 F3895F48233D2EEA00FA6A37 /* CodeSmsViewController.swift in Sources */,
@@ -932,33 +1180,46 @@
9321180 12BD95B8237EDB01009614D8 /* TimePointView.swift in Sources */,
9331181 12838EB2236779A000CEF108 /* AuthResolver.swift in Sources */,
9341182 12A075F72373535C00D749BB /* FirstStepReservationViewController.swift in Sources */,
1183+ 12E35078239FAFA4004BB188 /* ClientsListResolver.swift in Sources */,
9351184 1246B71C235CDF3A004CBFA1 /* ReservationCell.swift in Sources */,
1185+ 12E3507B239FCFB2004BB188 /* ProfileTableViewController.swift in Sources */,
9361186 1212048C23914CFD00CAF162 /* DeleteReservationResolver.swift in Sources */,
9371187 12A075FD237357E900D749BB /* FirstStepReservationCoordinator.swift in Sources */,
9381188 124D0081237222C900AE76B9 /* ReservationsService.swift in Sources */,
9391189 1246B720235CDF9E004CBFA1 /* ReservationView.swift in Sources */,
1190+ 12B3A7A523ABDA25009E2FA6 /* AdminReservationsCoordinator.swift in Sources */,
1191+ 123D25F4239E3FF5002FA32B /* ClientsViewController.swift in Sources */,
9401192 12050569238048CE00801618 /* Extensions+Date.swift in Sources */,
9411193 124D007C23721FD400AE76B9 /* Reservation.swift in Sources */,
9421194 12D8FA2F237F00E200305F57 /* DateView.swift in Sources */,
1195+ 123D25F2239E3E44002FA32B /* ClientsCoordinator.swift in Sources */,
9431196 12F9652E23869F3100FCA696 /* SecondStepReservationCoordinator.swift in Sources */,
9441197 F3895F4E233D348B00FA6A37 /* PhoneCoordinator.swift in Sources */,
9451198 12B6A617235BB37D00898B31 /* ChangeView.swift in Sources */,
9461199 12230AEC238FD8C5004A01E5 /* MapView2.swift in Sources */,
1200+ 12B3A7A323ABD594009E2FA6 /* AdminReservationsController.swift in Sources */,
9471201 F3FC7DCD2315BE610080D851 /* AppDelegate.swift in Sources */,
9481202 12838EB623677FB900CEF108 /* ApiRequest.swift in Sources */,
9491203 12118B9E23682049006BAEE3 /* PhoneCodeResolver.swift in Sources */,
9501204 F30369BE2336924D00A8EA84 /* ClientTabbarCoordinator.swift in Sources */,
9511205 F3FC7DFD2315C4710080D851 /* BaseCoordinator.swift in Sources */,
1206+ 12E35071239FA8CA004BB188 /* Extensions+String.swift in Sources */,
9521207 F3DC59FF233E893400B5E751 /* Extensions+UIFont.swift in Sources */,
9531208 F3895F53233D5EEB00FA6A37 /* BlackButtonStyle.swift in Sources */,
9541209 12F9652C23868E7200FCA696 /* SecondStepView.swift in Sources */,
9551210 120BFC7923891942001EBE89 /* ThirdStepReservationCoordinator.swift in Sources */,
1211+ 12B3A7BC23ACFD75009E2FA6 /* ReservationsAroundResolver.swift in Sources */,
9561212 12118B9C23682022006BAEE3 /* RegistrationResolver.swift in Sources */,
1213+ 12B3A7C423AD2FCD009E2FA6 /* AChangeReservationResolver.swift in Sources */,
9571214 F30369C02336925800A8EA84 /* ClientTabbarController.swift in Sources */,
1215+ 12B3A7C023AD0749009E2FA6 /* AdminReservationEditingCoordinator.swift in Sources */,
9581216 F31E20C72348DDB900BEA215 /* Extensions+UIView.swift in Sources */,
9591217 F3851B302340A513009EE0B6 /* RegistationViewModel.swift in Sources */,
1218+ 12B3A7AF23AC1872009E2FA6 /* AdminNoReservationsView.swift in Sources */,
9601219 12838EAE2367701300CEF108 /* AuthorizationService.swift in Sources */,
1220+ 12B3A7B723ACCD07009E2FA6 /* SettingsService.swift in Sources */,
9611221 12C72EDF23537D1B00AD7004 /* ProfileCoordinator.swift in Sources */,
1222+ 12E3507D239FD1F3004BB188 /* ClientReservationsResolver.swift in Sources */,
9621223 12EA994E2362FCE8005D9FE9 /* AddressView.swift in Sources */,
9631224 F3895F4C233D347E00FA6A37 /* PhoneNumberViewController.swift in Sources */,
9641225 12EE4A76234BB8FE005B4D1A /* CodeView.swift in Sources */,
@@ -969,6 +1230,7 @@
9691230 F3DC5A01233E896300B5E751 /* ButtonStyle.swift in Sources */,
9701231 12A075FF237358F200D749BB /* MapView.swift in Sources */,
9711232 1205056B2380588C00801618 /* Extenstions+IBDesignable.swift in Sources */,
1233+ 12E35074239FAF52004BB188 /* ClientsService.swift in Sources */,
9721234 12C72EE1235380D400AD7004 /* ProfileClientViewController.swift in Sources */,
9731235 F31E20C92348E0F300BEA215 /* AuthorizationViewController.swift in Sources */,
9741236 1212048E23915A0A00CAF162 /* ChangeReservationResolver.swift in Sources */,
@@ -976,8 +1238,10 @@
9761238 F3851B2923409F8A009EE0B6 /* PhoneView.swift in Sources */,
9771239 12C72EE3235380EB00AD7004 /* ReservationsViewController.swift in Sources */,
9781240 12B6A611235B8EC500898B31 /* User.swift in Sources */,
1241+ 12B3A7AD23ABEABF009E2FA6 /* AReservationsResolver.swift in Sources */,
9791242 12B4447C238EF25C000B6B16 /* CreateReservationResolver.swift in Sources */,
9801243 12B6A60F235B857A00898B31 /* ProfileChangeCoordinator.swift in Sources */,
1244+ 12B3A79C23ABB8ED009E2FA6 /* IntervalPickerView.swift in Sources */,
9811245 12FC40382382C93D00AB593B /* TableButtonStyle.swift in Sources */,
9821246 12D8FA31237F08C700305F57 /* AllReservations.swift in Sources */,
9831247 12838EB0236777D400CEF108 /* ServerError.swift in Sources */,
@@ -1138,14 +1402,15 @@
11381402 buildSettings = {
11391403 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
11401404 CODE_SIGN_STYLE = Automatic;
1141− DEVELOPMENT_TEAM = 9Z6LFGKA9G;
1405+ CURRENT_PROJECT_VERSION = 2;
1406+ DEVELOPMENT_TEAM = 23S94LRHD8;
11421407 INFOPLIST_FILE = "Hookah Tau/Info.plist";
11431408 IPHONEOS_DEPLOYMENT_TARGET = 11.0;
11441409 LD_RUNPATH_SEARCH_PATHS = (
11451410 "$(inherited)",
11461411 "@executable_path/Frameworks",
11471412 );
1148− PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau";
1413+ PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau1";
11491414 PRODUCT_NAME = "$(TARGET_NAME)";
11501415 SWIFT_VERSION = 5.0;
11511416 TARGETED_DEVICE_FAMILY = "1,2";
@@ -1157,14 +1422,15 @@
11571422 buildSettings = {
11581423 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
11591424 CODE_SIGN_STYLE = Automatic;
1160− DEVELOPMENT_TEAM = 9Z6LFGKA9G;
1425+ CURRENT_PROJECT_VERSION = 2;
1426+ DEVELOPMENT_TEAM = 23S94LRHD8;
11611427 INFOPLIST_FILE = "Hookah Tau/Info.plist";
11621428 IPHONEOS_DEPLOYMENT_TARGET = 11.0;
11631429 LD_RUNPATH_SEARCH_PATHS = (
11641430 "$(inherited)",
11651431 "@executable_path/Frameworks",
11661432 );
1167− PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau";
1433+ PRODUCT_BUNDLE_IDENTIFIER = "com.hookahtau.Hookah-Tau1";
11681434 PRODUCT_NAME = "$(TARGET_NAME)";
11691435 SWIFT_VERSION = 5.0;
11701436 TARGETED_DEVICE_FAMILY = "1,2";
Hookah Tau/API/Authorization api/AuthorizationService.swift+5
@@ -39,6 +39,7 @@ class AuthorizationService {
3939 }
4040
4141 completion(.success(decodedData.isUserRegistered))
42+ return
4243 }
4344 }
4445 }
@@ -65,6 +66,7 @@ class AuthorizationService {
6566
6667 self.setCookies(response: response)
6768 completion(.success(decodedData))
69+ return
6870 }
6971 }
7072 }
@@ -80,7 +82,9 @@ class AuthorizationService {
8082 apiClient.load(request: request.request) { result in
8183 switch result {
8284 case .failure(let err):
85+ print(err)
8386 completion(.failure(err))
87+ return
8488 case .success(let response):
8589 guard
8690 let unwrappedData = response.data,
@@ -92,6 +96,7 @@ class AuthorizationService {
9296
9397 self.setCookies(response: response)
9498 completion(.success(decodedData))
99+ return
95100 }
96101 }
97102 }
Hookah Tau/API/ClientsService/ClientsService.swiftnew file+66
@@ -0,0 +1,66 @@
1+//
2+// ClientsService.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+class ClientsService {
12+ private var apiClient: APIClient
13+
14+ init(apiClient: APIClient) {
15+ self.apiClient = apiClient
16+ }
17+
18+ func loadClientsList(completion: @escaping ((Result<[FullUser], GeneralError>) -> Void)) {
19+ let resolver = ClientsListResolver<ClientsResponce>()
20+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
21+
22+ apiClient.load(request: request.request) { (res) in
23+ switch res {
24+ case .success(let responce):
25+ guard
26+ let d = responce.data,
27+ let decodedData = resolver.targetClass().fromJSONToSelf(data: d)
28+ else {
29+ completion(.failure(GeneralError.decodeError))
30+ return }
31+
32+ completion(.success(decodedData.clients))
33+ return
34+ case .failure(let err):
35+ completion(.failure(err))
36+ return
37+ }
38+ }
39+ }
40+
41+ func loadReservationsForUser(withUUID uuid: String,
42+ completion: @escaping ((Result<[Reservation], GeneralError>) -> Void)) {
43+ let resolver = ClientReservationsResolver<UserReservationsResponce>(userId: uuid)
44+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
45+
46+ apiClient.load(request: request.request) { (res) in
47+ switch res {
48+ case .failure(let err):
49+ completion(.failure(err))
50+ return
51+ case .success(let responce):
52+ guard
53+ let d = responce.data,
54+ let decData = resolver.targetClass().fromJSONToSelf(data: d)
55+ else {
56+ completion(.failure(GeneralError.decodeError))
57+ return
58+ }
59+
60+ completion(.success(decData.reservations))
61+ return
62+ }
63+ }
64+
65+ }
66+}
Hookah Tau/API/ClientsService/Resolvers/ClientReservationsResolver.swiftnew file+34
@@ -0,0 +1,34 @@
1+//
2+// ClientReservations.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct UserReservationsResponce: MyCodable {
12+ var reservations: [Reservation]
13+}
14+
15+class ClientReservationsResolver<Responce: MyCodable>: ApiResolver {
16+
17+ var name: String {
18+ return "/clients/\(userId)/reservations"
19+ }
20+
21+ var groupName: String? {
22+ return "/admin"
23+ }
24+
25+ var userId: String
26+
27+ init(userId: String) {
28+ self.userId = userId
29+ }
30+
31+ func targetClass() -> Responce.Type {
32+ return Responce.self
33+ }
34+}
Hookah Tau/API/ClientsService/Resolvers/ClientsListResolver.swiftnew file+28
@@ -0,0 +1,28 @@
1+//
2+// ClientsListResolver.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct ClientsResponce: MyCodable {
12+ var clients: [FullUser]
13+}
14+
15+class ClientsListResolver<Responce: MyCodable>: ApiResolver {
16+
17+ var name: String {
18+ return "/clients"
19+ }
20+
21+ var groupName: String? {
22+ return "/admin"
23+ }
24+
25+ func targetClass() -> Responce.Type {
26+ return Responce.self
27+ }
28+}
Hookah Tau/API/Reservations api/ReservationsService.swift+55−1
@@ -60,14 +60,20 @@ class ReservationsService {
6060 numberOfGuests: Int,
6161 endTime: Date,
6262 uuid: String,
63+ isAdmin: Bool,
6364 completion: @escaping ((Bool) -> Void)) {
6465
6566 let reservation = ChangeReservationResolver.Request(startTime: startTime,
6667 uuid: uuid,
6768 numberOfGuests: numberOfGuests,
6869 endTime: endTime)
70+ var resolver: ApiResolver!
71+ if isAdmin {
72+ resolver = AChangeReservationResolver(data: reservation)
73+ } else {
74+ resolver = ChangeReservationResolver(data: reservation)
75+ }
6976
70− let resolver = ChangeReservationResolver(data: reservation)
7177 let request = ApiRequest(resolver: resolver, httpMethod: .put)
7278
7379 apiClient.load(request: request.request) { result in
@@ -145,4 +151,52 @@ class ReservationsService {
145151 }
146152 }
147153 }
154+
155+ func getAdminReservations(establishmentId: Int, completion: @escaping ((Result<[ReservationWithUser], GeneralError>)-> Void)) {
156+ let resolver = AReservationsResolver<AReservationResponse>(establishmentId: establishmentId)
157+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
158+
159+ apiClient.load(request: request.request) { (res) in
160+ switch res {
161+ case .failure(let err):
162+ completion(.failure(err))
163+ return
164+ case .success(let response):
165+ guard
166+ let d = response.data,
167+ let decodedData = resolver.targetClass().fromJSONToSelf(data: d)
168+ else {
169+ completion(.failure(GeneralError.decodeError))
170+ return
171+ }
172+
173+ completion(.success(decodedData.reservations))
174+ return
175+ }
176+ }
177+ }
178+
179+ func getReservationsAround(uuid: String, completion: @escaping((Result<[ReservationPeriod], GeneralError>) -> Void)) {
180+ let resolver = ReservationsAroundResolver<ReservationsAroundResponse>(reservationId: uuid)
181+ let request = ApiRequest(resolver: resolver, httpMethod: .get)
182+
183+ apiClient.load(request: request.request) { (res) in
184+ switch res {
185+ case .failure(let err):
186+ completion(.failure(err))
187+ return
188+ case .success(let response):
189+ guard
190+ let d = response.data,
191+ let decodedData = resolver.targetClass().fromJSONToSelf(data: d)
192+ else {
193+ completion(.failure(GeneralError.decodeError))
194+ return
195+ }
196+
197+ completion(.success(decodedData.reservations))
198+ return
199+ }
200+ }
201+ }
148202}
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/AReservationsResolver.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// AReservationsResolver.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 Foundation
10+
11+struct AReservationResponse: MyCodable {
12+ var reservations: [ReservationWithUser]
13+}
14+
15+/// `/admin/reservations/{establishment id}`
16+class AReservationsResolver<Response: MyCodable>: ApiResolver {
17+
18+ var name: String {
19+ return "/reservations/\(establishmentId)"
20+ }
21+
22+ var groupName: String? {
23+ return "/admin"
24+ }
25+
26+ var establishmentId: Int
27+
28+ init(establishmentId: Int) {
29+ self.establishmentId = establishmentId
30+ }
31+
32+ func targetClass() -> Response.Type {
33+ return Response.self
34+ }
35+}
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/Settings/Resolvers/AddOtherAdminResolver.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// AddOtherAdminResolver.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+/// `PUT /admin/other`
12+class AddOtherAdminResolver: ApiResolver {
13+ /// Request struct
14+ struct Request: MyCodable {
15+ var newAdmin: String
16+ }
17+
18+ var name: String {
19+ return "/other"
20+ }
21+
22+ var groupName: String? {
23+ return "/admin"
24+ }
25+
26+ var phone: String
27+
28+ init(phone: String) {
29+ self.phone = phone
30+ }
31+
32+ func parameters() -> MyCodable? {
33+ return Request(newAdmin: phone)
34+ }
35+}
Hookah Tau/API/Settings/SettingsService.swiftnew file+34
@@ -0,0 +1,34 @@
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/Controllers/Admin/Clients/Cells/ClientTableViewCell.swiftnew file+44
@@ -0,0 +1,44 @@
1+//
2+// ClientTableViewCell.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 09/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+let clientTableViewCellId = "ClientTableViewCell"
12+
13+class ClientTableViewCell: UITableViewCell {
14+
15+ // MARK: - IBAOutlets
16+
17+ @IBOutlet weak var nameLabel: UILabel!
18+
19+ @IBOutlet weak var phoneNumberLabel: UILabel!
20+
21+ @IBOutlet weak var numberOfReservationsLabel: UILabel!
22+
23+ // MARK: - Lifecycle
24+
25+ override func awakeFromNib() {
26+ super.awakeFromNib()
27+ // Initialization code
28+ }
29+
30+ override func setSelected(_ selected: Bool, animated: Bool) {
31+ super.setSelected(selected, animated: animated)
32+
33+ // Configure the view for the selected state
34+ }
35+
36+ // MARK: - Bind
37+
38+ func bind(withModel model: FullUser) {
39+ let name = model.name ?? "❔"
40+ nameLabel.text = model.isAdmin ? "👑 " + name : name
41+ phoneNumberLabel.text = model.phoneNumber.formattedNumber()
42+ }
43+
44+}
Hookah Tau/Controllers/Admin/Clients/ClientList.storyboardnew file+149
@@ -0,0 +1,149 @@
1+<?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">
3+ <device id="retina4_7" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
7+ <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <scenes>
11+ <!--Clients View Controller-->
12+ <scene sceneID="U7X-Jx-osU">
13+ <objects>
14+ <tableViewController storyboardIdentifier="ClientsViewController" id="Wfj-Ux-iLR" customClass="ClientsViewController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
15+ <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="qFn-ia-vVI">
16+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
17+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
19+ <prototypes>
20+ <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="ClientTableViewCell" rowHeight="69" id="QPb-0v-yOO" customClass="ClientTableViewCell" customModule="Hookah_Tau" customModuleProvider="target">
21+ <rect key="frame" x="0.0" y="28" width="375" height="69"/>
22+ <autoresizingMask key="autoresizingMask"/>
23+ <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QPb-0v-yOO" id="48D-Q7-dBg">
24+ <rect key="frame" x="0.0" y="0.0" width="375" height="69"/>
25+ <autoresizingMask key="autoresizingMask"/>
26+ <subviews>
27+ <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="fhd-NM-rld">
28+ <rect key="frame" x="16" y="8" width="343" height="53"/>
29+ <subviews>
30+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="vNV-bN-3No">
31+ <rect key="frame" x="0.0" y="0.0" width="179" height="53"/>
32+ <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+ <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>
39+ <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">
40+ <rect key="frame" x="0.0" y="29" width="179" height="24"/>
41+ <fontDescription key="fontDescription" type="system" pointSize="20"/>
42+ <nil key="textColor"/>
43+ <nil key="highlightedColor"/>
44+ </label>
45+ </subviews>
46+ </stackView>
47+ <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"/>
49+ <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+ <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"/>
58+ </imageView>
59+ </subviews>
60+ </stackView>
61+ </subviews>
62+ </stackView>
63+ </subviews>
64+ <constraints>
65+ <constraint firstItem="fhd-NM-rld" firstAttribute="leading" secondItem="48D-Q7-dBg" secondAttribute="leading" constant="16" id="EDr-e5-ffF"/>
66+ <constraint firstAttribute="trailing" secondItem="fhd-NM-rld" secondAttribute="trailing" constant="16" id="jMD-pP-Fcj"/>
67+ <constraint firstAttribute="bottom" secondItem="fhd-NM-rld" secondAttribute="bottom" constant="8" id="lGU-2k-95p"/>
68+ <constraint firstItem="fhd-NM-rld" firstAttribute="top" secondItem="48D-Q7-dBg" secondAttribute="top" constant="8" id="yPf-vR-dW7"/>
69+ </constraints>
70+ </tableViewCellContentView>
71+ <connections>
72+ <outlet property="nameLabel" destination="4Cp-jh-0Gq" id="g6e-c0-x0u"/>
73+ <outlet property="numberOfReservationsLabel" destination="Gzo-kW-60Z" id="9J2-S4-fKq"/>
74+ <outlet property="phoneNumberLabel" destination="0gY-h5-AV2" id="rdb-GS-ceF"/>
75+ </connections>
76+ </tableViewCell>
77+ </prototypes>
78+ <connections>
79+ <outlet property="dataSource" destination="Wfj-Ux-iLR" id="uby-48-VjA"/>
80+ <outlet property="delegate" destination="Wfj-Ux-iLR" id="oeU-I8-pfI"/>
81+ </connections>
82+ </tableView>
83+ </tableViewController>
84+ <placeholder placeholderIdentifier="IBFirstResponder" id="epE-4n-L3K" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
85+ </objects>
86+ <point key="canvasLocation" x="0.80000000000000004" y="-112.89355322338831"/>
87+ </scene>
88+ <!--Profile Table View Controller-->
89+ <scene sceneID="8Qr-sA-YQr">
90+ <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">
93+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
94+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
95+ <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>
136+ <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"/>
139+ </connections>
140+ </tableViewController>
141+ <placeholder placeholderIdentifier="IBFirstResponder" id="1YW-I1-Y6m" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
142+ </objects>
143+ <point key="canvasLocation" x="880.79999999999995" y="-111.0944527736132"/>
144+ </scene>
145+ </scenes>
146+ <resources>
147+ <image name="arrow" width="12" height="20"/>
148+ </resources>
149+</document>
Hookah Tau/Controllers/Admin/Clients/ClientsCoordinator.swiftnew file+36
@@ -0,0 +1,36 @@
1+//
2+// ClientsCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 09/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+
12+class ClientsCoordinator: BaseCoordinator {
13+
14+ override func start() {
15+ let storyboard = UIStoryboard(name: "ClientList", bundle: nil)
16+ let viewController = storyboard.instantiateViewController(withIdentifier: "ClientsViewController") as? ClientsViewController
17+ viewController?.coordinator = self
18+ guard let vc = viewController else { return }
19+ navigationController?.navigationBar.prefersLargeTitles = true
20+ navigationController?.viewControllers = [vc]
21+ }
22+
23+ /// TODO
24+ func openUserProfile(_ profile: FullUser) {
25+ let profileCoordinator = ClientProfileCoordinator(navigationController: navigationController)
26+ profileCoordinator.user = profile
27+
28+ self.addDependency(profileCoordinator)
29+ profileCoordinator.didEndFlow = {
30+ self.removeDependency(profileCoordinator)
31+ self.navigationController?.popViewController(animated: true)
32+ }
33+
34+ profileCoordinator.start()
35+ }
36+}
Hookah Tau/Controllers/Admin/Clients/ClientsViewController.swiftnew file+72
@@ -0,0 +1,72 @@
1+//
2+// ClientsViewController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 09/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class ClientsViewController: UITableViewController {
12+
13+ // MARK: - Properties
14+
15+ var dataSource: [FullUser] = [] {
16+ didSet {
17+ print(dataSource)
18+ tableView.reloadData()
19+ }
20+ }
21+
22+ var clientsService: ClientsService?
23+
24+ weak var coordinator: ClientsCoordinator?
25+
26+ // MARK: - Lifecycle
27+
28+ override func viewDidLoad() {
29+ super.viewDidLoad()
30+
31+ navigationItem.title = "Клиенты"
32+ clientsService = ClientsService(apiClient: APIClient.shared)
33+ }
34+
35+ override func viewDidAppear(_ animated: Bool) {
36+ super.viewDidAppear(animated)
37+ loadClients()
38+ }
39+
40+ /// Загружаем клиентов кальянки
41+ func loadClients() {
42+ clientsService?.loadClientsList(completion: { [ weak self ] (res) in
43+ switch res {
44+ case .failure(let err):
45+ print(err)
46+ case .success(let arr):
47+ self?.dataSource = arr
48+ }
49+ })
50+ }
51+
52+
53+ // MARK: - Tableview
54+
55+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
56+ let cell = tableView.dequeueReusableCell(withIdentifier: clientTableViewCellId, for: indexPath) as! ClientTableViewCell
57+
58+ let userModel = dataSource[indexPath.row]
59+ cell.bind(withModel: userModel)
60+
61+ return cell
62+ }
63+
64+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
65+ return dataSource.count
66+ }
67+
68+ override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
69+ let user = dataSource[indexPath.row]
70+ coordinator?.openUserProfile(user)
71+ }
72+}
Hookah Tau/Controllers/Admin/Clients/Profile/ClientProfileCoordinator.swiftnew file+29
@@ -0,0 +1,29 @@
1+//
2+// ClientProfileCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+
12+class ClientProfileCoordinator: BaseCoordinator {
13+
14+ var user: FullUser?
15+
16+ override func start() {
17+ let storyboard = UIStoryboard(name: "ClientList", bundle: nil)
18+ let viewController = storyboard.instantiateViewController(withIdentifier: "ProfileTableViewController") as? ProfileTableViewController
19+ viewController?.user = user
20+ guard let vc = viewController else { return }
21+ navigationController?.pushViewController(vc, animated: true)
22+ }
23+
24+
25+ // Создание бронирования из профиля клиента
26+ func createReservation(forUserWithPhone phoneNumber: String) {
27+
28+ }
29+}
Hookah Tau/Controllers/Admin/Clients/Profile/ProfileTableViewController.swiftnew file+98
@@ -0,0 +1,98 @@
1+//
2+// ProfileTableViewController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class ProfileTableViewController: UITableViewController {
12+
13+ // MARK: - IBAOutlets
14+
15+ @IBOutlet weak var phoneButton: Button! {
16+ didSet {
17+ guard let u = user else { return }
18+
19+ let style = BlackButtonStyle()
20+ style.apply(to: phoneButton, withTitle: u.phoneNumber.formattedNumber())
21+ phoneButton.addTarget(self, action: #selector(callUser), for: .touchUpInside)
22+ }
23+ }
24+
25+ @IBOutlet weak var copyButton: Button! {
26+ didSet {
27+ let style = BlackButtonStyle()
28+ style.apply(to: copyButton, withTitle: "СКОПИРОВАТЬ")
29+ }
30+ }
31+
32+ // MARK: - properties
33+
34+ var clientsService: ClientsService?
35+
36+ var user: FullUser? {
37+ didSet {
38+ guard let u = user else { return }
39+ navigationItem.title = u.name
40+ }
41+ }
42+
43+ weak var coodinator: ClientProfileCoordinator?
44+
45+ var dataSource: [Reservation] = [] {
46+ didSet {
47+ tableView.reloadData()
48+ }
49+ }
50+
51+ // MARK: - Lifecycle
52+
53+ override func viewDidLoad() {
54+ super.viewDidLoad()
55+
56+ tableView?.register(ReservationCell.self, forCellReuseIdentifier: reservationCell)
57+
58+ clientsService = ClientsService(apiClient: APIClient.shared)
59+ loadReservations()
60+ }
61+
62+ func loadReservations() {
63+ guard let uuid = user?.uuid else { return }
64+ clientsService?.loadReservationsForUser(withUUID: uuid,
65+ completion: { [weak self] (res) in
66+ switch res {
67+ case .failure(let err):
68+ print(err)
69+ case .success(let data):
70+ self?.dataSource = data
71+ }
72+ })
73+ }
74+
75+ // MARK: - Table view data source
76+
77+ override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
78+ return dataSource.count
79+ }
80+
81+ override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
82+ let cell = tableView.dequeueReusableCell(withIdentifier: reservationCell, for: indexPath) as! ReservationCell
83+
84+ cell.bind(withModel: dataSource[indexPath.row])
85+
86+ return cell
87+ }
88+
89+ @objc func callUser() {
90+ guard
91+ let u = user,
92+ let urlPhone = URL(string: "tel://79\(u.phoneNumber)")
93+ else { return }
94+
95+ UIApplication.shared.open(urlPhone)
96+ }
97+
98+}
Hookah Tau/Controllers/Admin/Reservations/AdminAddressCoordinator.swiftnew file+35
@@ -0,0 +1,35 @@
1+//
2+// AdminAddressCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 09/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+class AdminAddressCoordinator: BaseCoordinator {
12+
13+ override func start() {
14+ let addressViewController = AddresViewController()
15+ addressViewController.coordinator = self
16+ navigationController?.viewControllers = [addressViewController]
17+
18+ guard let id = DataStorage.standard.getEstablishmentChoice() else { return }
19+ openReservations(forEstablishment: id)
20+ }
21+
22+ private func openReservations(forEstablishment id: Int) {
23+ let reservationsCoordinator = AdminReservationsCoordinator(navigationController: navigationController)
24+ addDependency(reservationsCoordinator)
25+ reservationsCoordinator.establishmentId = id
26+ reservationsCoordinator.start()
27+ }
28+}
29+
30+extension AdminAddressCoordinator: AddressMapper {
31+ func chooseAddress(establishmentId id: Int) {
32+ DataStorage.standard.setupEstablishmentChoice(id: id)
33+ openReservations(forEstablishment: id)
34+ }
35+}
Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingCoordinator.swiftnew file+29
@@ -0,0 +1,29 @@
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 {
13+
14+ // MARK: - Peoperties
15+
16+ var reservationData: ReservationWithUser?
17+
18+ // MARK: - Lifecycle
19+
20+ override func start() {
21+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
22+ let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationEditingViewController") as! AdminReservationEditingViewController
23+ vc.reservationData = reservationData
24+ vc.coordinator = self
25+
26+ navigationController?.pushViewController(vc, animated: true)
27+ }
28+
29+}
Hookah Tau/Controllers/Admin/Reservations/AdminReservationEditingViewController.swiftnew file+139
@@ -0,0 +1,139 @@
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: AdminReservationEditingCoordinator?
28+
29+ var reservationService: ReservationsService?
30+
31+ var reservedPeriods: [ReservationPeriod] = [] {
32+ didSet {
33+ setupReservationViewWithIntervals()
34+ }
35+ }
36+
37+ var saveButton: Button = {
38+ let button = Button()
39+ let style = BlackButtonStyle()
40+ style.apply(to: button, withTitle: "СОХРАНИТЬ")
41+ return button
42+ }()
43+
44+ // MARK:- Lifecycle
45+
46+ override func viewDidLoad() {
47+ super.viewDidLoad()
48+
49+ setupContentViews()
50+
51+ reservationService = ReservationsService(apiClient: APIClient.shared)
52+
53+ loadReservedPeriods()
54+
55+ setupButtons()
56+ }
57+
58+ override func viewWillAppear(_ animated: Bool) {
59+ super.viewWillAppear(animated)
60+
61+ tabBarController?.tabBar.isHidden = true
62+ }
63+
64+ // MARK: - Setup
65+
66+ func setupButtons() {
67+ addStackViewWithButtons(rightBtn: saveButton,
68+ constant: -mainContentView.frame.height - 10)
69+
70+ saveButton.addTarget(self, action: #selector(saveReservation), for: .touchUpInside)
71+ }
72+
73+ func setupContentViews() {
74+ guard
75+ let model = reservationData
76+ else { return }
77+
78+ if model.establishment == 1 {
79+ mapView = MapView1.loadFromNib()
80+ } else {
81+ mapView = MapView2.loadFromNib()
82+ }
83+
84+ mapView?.isUserInteractionEnabled = false
85+
86+ tableContentView.addSubviewThatFills(mapView)
87+ mapView?.scrollToTable(table: model.reservedTable)
88+
89+ reservationView = AdminReservationView.loadFromNib()
90+ reservationView?.bind(withModel: model)
91+
92+ mainContentView.addSubviewThatFills(reservationView)
93+ }
94+
95+ func setupReservationViewWithIntervals() {
96+ reservationView?.bind(withIntervals: reservedPeriods)
97+ }
98+
99+ func loadReservedPeriods() {
100+ guard let model = reservationData else { return }
101+
102+ reservationService?.getReservationsAround(uuid: model.uuid, completion: { [weak self] (res) in
103+ switch res {
104+ case .failure(let err):
105+ self?.displayAlert(forError: err)
106+ return
107+ case .success(let reserv):
108+ self?.reservedPeriods = reserv
109+ return
110+ }
111+ })
112+ }
113+
114+ // MARK:- Handlers
115+
116+ @objc func saveReservation() {
117+ guard
118+ let r = reservationView?.data,
119+ let uuid = reservationData?.uuid
120+ else { return }
121+
122+ saveButton.loading = true
123+
124+ reservationService?.updateReservation(startTime: r.startTime,
125+ numberOfGuests: r.numberOfGuests,
126+ endTime: r.endTime,
127+ uuid: uuid,
128+ isAdmin: true,
129+ completion: { [weak self] (res) in
130+ if res {
131+ self?.coordinator?.didEndFlow?()
132+ } else {
133+ self?.displayAlert(with: "Что-то пошло не так! Попробуешь еще раз?")
134+ }
135+
136+ self?.saveButton.loading = false
137+ })
138+ }
139+}
Hookah Tau/Controllers/Admin/Reservations/AdminReservations.storyboardnew file+68
@@ -0,0 +1,68 @@
1+<?xml version="1.0" encoding="UTF-8"?>
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">
3+ <device id="retina4_7" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <scenes>
11+ <!--Admin Reservations Controller-->
12+ <scene sceneID="3G1-PQ-PoW">
13+ <objects>
14+ <viewController storyboardIdentifier="AdminReservationsController" id="Imn-L1-YIQ" customClass="AdminReservationsController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
15+ <view key="view" contentMode="scaleToFill" id="DoI-oD-1Zy">
16+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
17+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
19+ <viewLayoutGuide key="safeArea" id="QBQ-KH-Pid"/>
20+ </view>
21+ </viewController>
22+ <placeholder placeholderIdentifier="IBFirstResponder" id="Rnh-3m-3dv" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
23+ </objects>
24+ <point key="canvasLocation" x="111" y="159"/>
25+ </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="206" width="375" height="441"/>
36+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
37+ <constraints>
38+ <constraint firstAttribute="height" constant="441" 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="198"/>
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+ </scenes>
68+</document>
Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsController.swiftnew file+145
@@ -0,0 +1,145 @@
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 activeReservations: [ReservationWithUser] = [] {
37+ didSet {
38+ guard let empty = noReservationsView else { return }
39+ if activeReservations.isEmpty {
40+ contentView.bringSubviewToFront(empty)
41+ empty.alpha = 1
42+ } else {
43+ contentView.sendSubviewToBack(empty)
44+ tableView?.alpha = 1
45+ }
46+
47+ tableView?.reloadData()
48+ }
49+ }
50+
51+ override func viewDidLoad() {
52+ super.viewDidLoad()
53+
54+ reservationsService = ReservationsService(apiClient: APIClient.shared)
55+ setUpContentView()
56+ setupNavBar()
57+ }
58+
59+ override func viewDidAppear(_ animated: Bool) {
60+ super.viewDidAppear(animated)
61+
62+ performUpdate()
63+ tabBarController?.tabBar.isHidden = false
64+ }
65+
66+ // MARK:- Setup
67+
68+ func setupNavBar() {
69+ self.navigationItem.title = "Брони"
70+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(book))
71+ }
72+
73+ func setUpContentView() {
74+ self.view.addSubviewThatFills(contentView)
75+
76+ // empty
77+ noReservationsView = AdminNoReservationsView.loadFromNib()
78+ contentView.addSubviewThatFills(noReservationsView)
79+ noReservationsView?.bookButton?.addTarget(self, action: #selector(book), for: .touchUpInside)
80+
81+ noReservationsView?.alpha = 0
82+
83+ // table
84+ contentView.addSubviewThatFills(tableView)
85+
86+ setUpTableView()
87+ }
88+
89+ func setUpTableView() {
90+ tableView?.delegate = self
91+ tableView?.dataSource = self
92+
93+ tableView?.alpha = 0
94+
95+ tableView?.register(AdminReservationCell.self, forCellReuseIdentifier: adminReservationCellId)
96+ }
97+
98+ func performUpdate() {
99+
100+ guard let id = id else { return }
101+
102+ reservationsService?.getAdminReservations(establishmentId: id, completion: { [weak self] (result) in
103+ switch result {
104+ case .success(let reservations):
105+ self?.activeReservations = reservations
106+ case .failure(let err):
107+ self?.displayAlert(forError: err)
108+ }
109+ })
110+ }
111+
112+ // MARK:- Button handler
113+
114+ @objc func book() {
115+
116+ }
117+
118+}
119+
120+
121+// MARK: - Table view data source
122+
123+extension AdminReservationsController: UITableViewDelegate, UITableViewDataSource {
124+
125+ func numberOfSections(in tableView: UITableView) -> Int {
126+ return 1
127+ }
128+
129+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
130+ return activeReservations.count
131+ }
132+
133+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
134+ let cell = tableView.dequeueReusableCell(withIdentifier: adminReservationCellId, for: indexPath) as! AdminReservationCell
135+
136+ cell.bind(withModel: activeReservations[indexPath.row])
137+ return cell
138+ }
139+
140+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
141+ let reservation = activeReservations[indexPath.row]
142+ coordinator?.seeExistingReservation(data: reservation)
143+ }
144+
145+}
Hookah Tau/Controllers/Admin/Reservations/List of reservations/AdminReservationsCoordinator.swiftnew file+38
@@ -0,0 +1,38 @@
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 establishmentId: Int?
16+
17+ // MARK: - Lifecycle
18+
19+ override func start() {
20+ let storyboard = UIStoryboard(name: "AdminReservations", bundle: nil)
21+ let vc = storyboard.instantiateViewController(withIdentifier: "AdminReservationsController") as! AdminReservationsController
22+ vc.id = establishmentId
23+ vc.coordinator = self
24+
25+ navigationController?.pushViewController(vc, animated: true)
26+ }
27+
28+ func seeExistingReservation(data: ReservationWithUser) {
29+ let reservationCoordinator = AdminReservationEditingCoordinator(navigationController: navigationController)
30+ addDependency(reservationCoordinator)
31+ reservationCoordinator.reservationData = data
32+ reservationCoordinator.didEndFlow = { [weak self] in
33+ self?.removeDependency(reservationCoordinator)
34+ self?.navigationController?.popViewController(animated: true)
35+ }
36+ reservationCoordinator.start()
37+ }
38+}
Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.swiftnew file+84
@@ -0,0 +1,84 @@
1+//
2+// AdminReservationCell.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+let adminReservationCellId = "AdminReservationCell"
12+
13+class AdminReservationCell: UITableViewCell {
14+ // MARK: - Properties
15+
16+ var reservationCellView: AdminReservationCellView?
17+
18+ // MARK: - Init
19+
20+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
21+ {
22+ super.init(style: style, reuseIdentifier: reuseIdentifier)
23+
24+ reservationCellView = AdminReservationCellView.loadFromNib()
25+ contentView.addSubviewThatFills(reservationCellView)
26+
27+ self.selectionStyle = .none
28+ }
29+
30+ required init?(coder aDecoder: NSCoder)
31+ {
32+ fatalError("init(coder:) has not been implemented")
33+ }
34+
35+ // MARK: - Data
36+
37+ func bind(withModel model: ReservationWithUser) {
38+ reservationCellView?.bind(withModel: model)
39+ }
40+}
41+
42+class AdminReservationCellView: UIView {
43+
44+ // MARK:- IBAOutlets
45+
46+ @IBOutlet weak var tableImage: UIImageView?
47+
48+ @IBOutlet weak var guestNumber: UILabel?
49+
50+ @IBOutlet weak var dateLabel: UILabel?
51+
52+ @IBOutlet weak var timeLabel: UILabel?
53+
54+ @IBOutlet weak var personLabel: UILabel?
55+
56+ // MARK: - Lifecycle
57+
58+ override func awakeFromNib() {
59+ super.awakeFromNib()
60+ // Initialization code
61+ }
62+
63+ // MARK: - bind
64+
65+ func bind(withModel model: ReservationWithUser) {
66+ let (date, time) = Date.format(model.startTime, model.endTime)
67+
68+ dateLabel?.text = date
69+ timeLabel?.text = time
70+
71+ guestNumber?.text = setPeople(number: model.numberOfGuests)
72+ personLabel?.text = model.owner.name ?? "❔❔❔"
73+ tableImage?.image = TotalStorage.standard.getTable(establishment: model.establishment, table: model.reservedTable)?.image
74+ }
75+
76+ func setPeople(number: Int) -> String {
77+ switch number {
78+ case 2...4:
79+ return "\(number) человека"
80+ default:
81+ return "\(number) человек"
82+ }
83+ }
84+}
Hookah Tau/Controllers/Admin/Reservations/List of reservations/Cell/AdminReservationCellView.xibnew file+93
@@ -0,0 +1,93 @@
1+<?xml version="1.0" encoding="UTF-8"?>
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="retina4_7" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="Stack View standard spacing" minToolsVersion="9.0"/>
9+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
10+ </dependencies>
11+ <objects>
12+ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
13+ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
14+ <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"/>
16+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
17+ <subviews>
18+ <stackView opaque="NO" contentMode="scaleToFill" spacingType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="au9-nK-XbR">
19+ <rect key="frame" x="17" y="3" width="380" height="133"/>
20+ <subviews>
21+ <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="254" height="133"/>
23+ <subviews>
24+ <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">
25+ <rect key="frame" x="0.0" y="0.0" width="54" height="19.5"/>
26+ <fontDescription key="fontDescription" type="system" pointSize="16"/>
27+ <color key="textColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="calibratedRGB"/>
28+ <nil key="highlightedColor"/>
29+ </label>
30+ <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"/>
32+ <subviews>
33+ <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">
34+ <rect key="frame" x="0.0" y="0.0" width="76.5" height="20.5"/>
35+ <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
36+ <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
37+ <nil key="highlightedColor"/>
38+ </label>
39+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3:00 — 5:30" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IdW-5d-DS0">
40+ <rect key="frame" x="0.0" y="24.5" width="102" height="20.5"/>
41+ <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
42+ <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
43+ <nil key="highlightedColor"/>
44+ </label>
45+ </subviews>
46+ </stackView>
47+ <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"/>
49+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
50+ <nil key="textColor"/>
51+ <nil key="highlightedColor"/>
52+ </label>
53+ </subviews>
54+ </stackView>
55+ <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"/>
57+ </imageView>
58+ </subviews>
59+ </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>
67+ </subviews>
68+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
69+ <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"/>
77+ </constraints>
78+ <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
79+ <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
80+ <connections>
81+ <outlet property="dateLabel" destination="9k3-E5-XKR" id="wBe-yq-PN9"/>
82+ <outlet property="guestNumber" destination="VhL-qH-zM9" id="Ent-pM-uER"/>
83+ <outlet property="personLabel" destination="KV8-UG-2ws" id="eYB-qn-fJL"/>
84+ <outlet property="tableImage" destination="Oa6-n7-joV" id="cyD-bY-OpP"/>
85+ <outlet property="timeLabel" destination="IdW-5d-DS0" id="ddO-Dx-LDK"/>
86+ </connections>
87+ <point key="canvasLocation" x="137.59999999999999" y="90.854572713643179"/>
88+ </view>
89+ </objects>
90+ <resources>
91+ <image name="1_7" width="118" height="133"/>
92+ </resources>
93+</document>
Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.swiftnew file+364
@@ -0,0 +1,364 @@
1+//
2+// AdminReservationView.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 AdminReservationView: UIView {
12+
13+ // MARK:- IBAOutlets
14+
15+ @IBOutlet weak var tableLabel: UILabel?
16+
17+ @IBOutlet weak var dateLabel: UILabel?
18+
19+ @IBOutlet weak var timeLabel: UILabel?
20+
21+ @IBOutlet weak var cancelButton: Button? {
22+ didSet {
23+ let style = BlackButtonStyle()
24+ style.apply(to: cancelButton, withTitle: "ОТМЕНИТЬ")
25+ }
26+ }
27+
28+ @IBOutlet weak var nameLabel: UILabel?
29+
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+ // MARK: - Properties
43+
44+ // констрейнты начала и конца интервала
45+ var startLeadingConstraint: NSLayoutConstraint!
46+ var endLeadingConstraint: NSLayoutConstraint!
47+
48+ var startPoint: UIImageView = {
49+ var view = UIImageView()
50+ view.isUserInteractionEnabled = true
51+ view.image = #imageLiteral(resourceName: "handle_left")
52+ view.translatesAutoresizingMaskIntoConstraints = false
53+ return view
54+ }()
55+
56+ var endPoint: UIImageView = {
57+ var view = UIImageView()
58+ view.isUserInteractionEnabled = true
59+ view.image = #imageLiteral(resourceName: "Group")
60+ view.translatesAutoresizingMaskIntoConstraints = false
61+ return view
62+ }()
63+
64+ /// Интервал
65+ var fillInterval: UIView = {
66+ var view = UIView()
67+ view.backgroundColor = #colorLiteral(red: 0, green: 0.5921568627, blue: 1, alpha: 0.3426262843)
68+ view.translatesAutoresizingMaskIntoConstraints = false
69+ return view
70+ }()
71+
72+ /// Время начала вертелки.
73+ /// Для клиента это 12:00 смены (надо вызвать метод для получения текущей смены).
74+ /// Для администратора это `t` - 24h
75+ var startDate: Date?
76+
77+ // MARK:- Properties
78+
79+ var intervalPickerView: IntervalPickerView?
80+
81+ /// Модель вьюшки.
82+ /// Обновляем каждый раз когда меняется время и дата
83+ var data: ReservationData?
84+
85+ var model: ReservationWithUser?
86+
87+ // MARK: - Setup
88+
89+ func bind(withModel model: ReservationWithUser) {
90+ let style = BlackButtonStyle()
91+ style.apply(to: callButton, withTitle: model.owner.phoneNumber.formattedNumber())
92+
93+
94+ let (date, time) = Date.format(model.startTime, model.endTime)
95+ dateLabel?.text = date
96+ timeLabel?.text = time
97+
98+ tableLabel?.text = "\(model.reservedTable) столик"
99+ nameLabel?.text = "\(model.owner.name ?? "❔")"
100+
101+ data = ReservationData(establishment: model.establishment,
102+ startTime: model.startTime,
103+ endTime: model.endTime,
104+ numberOfGuests: model.numberOfGuests,
105+ reservedTable: model.reservedTable)
106+ self.model = model
107+
108+ let (start, _) = model.owner.isAdmin ? model.startTime.getAdminStartingAndEndPoint() : model.startTime.getClientStartingAndEndPoint()
109+
110+ startDate = start
111+
112+ setUpIntervals(model.owner.isAdmin)
113+
114+ guestSlider?.maximumValue = Float((TotalStorage.standard.getTable(establishment: model.establishment, table: model.reservedTable)?.maxGuestNumber ?? 2) + 1)
115+ }
116+
117+ func bind(withIntervals intervals: [ReservationPeriod]) {
118+ guard
119+ let data = data,
120+ let isAdmin = model?.owner.isAdmin
121+ else { return }
122+
123+ setUpReservedIntervals(intervals, isAdmin)
124+ setUpBookingPeriod(data.startTime, data.endTime)
125+ }
126+
127+ func updateDateAndTime(withData d: ReservationData?) {
128+ guard let d = d else { return }
129+ let (date, time) = Date.format(d.startTime, d.endTime)
130+
131+ dateLabel?.text = date
132+ timeLabel?.text = time
133+ }
134+
135+ @IBAction func valueChanged(_ sender: UISlider) {
136+ let value = Int(sender.value)
137+
138+ if sender.value == sender.maximumValue {
139+ numberOfGuestLabel?.text = "Количество гостей: \(Int(value) - 1)+"
140+ } else {
141+ numberOfGuestLabel?.text = "Количество гостей: \(value)"
142+ }
143+
144+ guard let d = data else { return }
145+
146+ data = ReservationData(establishment: d.establishment,
147+ startTime: d.startTime,
148+ endTime: d.endTime,
149+ numberOfGuests: value,
150+ reservedTable: d.reservedTable)
151+ }
152+
153+
154+ func getNumberOfCalians(_ sliderValue: Int) -> String {
155+ return "Минимальное количество кальянов: \(sliderValue > 6 ? 2 : 1)"
156+ }
157+
158+ // MARK: - Setup
159+
160+ /// Тут происходит установка интервалов. Разное для клиента и админа
161+ func setUpIntervals(_ isAdmin: Bool) {
162+ guard let startPoint = startDate else { return }
163+
164+ let startingPoint = isAdmin ? startPoint.getAdminsStartingPoint() : Constants.clientStartingPoint
165+ let endingPoint = startingPoint + (isAdmin ? Constants.adminDuration : Constants.clientDuration)
166+
167+ for period in startingPoint...endingPoint {
168+ let timePointView = TimePointView.loadFromNib()
169+ timePointView?.setUp(withTimePoint: period)
170+ let view = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: 106))
171+ view.addSubviewThatFills(timePointView)
172+ intervalsStackView.addArrangedSubview(view)
173+ }
174+ }
175+
176+
177+ private func setUpBookingPeriod(_ startTime: Date, _ endTime: Date? = nil) {
178+ self.scrollView.addSubview(startPoint)
179+ self.scrollView.addSubview(endPoint)
180+ self.scrollView.addSubview(fillInterval)
181+
182+ guard let startDate = startDate else { return }
183+
184+ let startConstr = CGFloat(startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
185+
186+ scrollView.setContentOffset(CGPoint(x: startConstr - 50, y: 0), animated: true)
187+
188+ startLeadingConstraint = startPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
189+ constant: startConstr)
190+
191+ if let end = endTime {
192+ let endConstr = CGFloat(end.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
193+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
194+ constant: endConstr)
195+ } else {
196+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
197+ constant:startConstr + Constants.minBookingLength)
198+ }
199+
200+ NSLayoutConstraint.activate([
201+
202+ startPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
203+ endPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
204+
205+ startPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
206+ endPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
207+
208+ startPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
209+ endPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
210+
211+ startLeadingConstraint,
212+ endLeadingConstraint,
213+
214+ fillInterval.leftAnchor.constraint(equalTo: startPoint.rightAnchor, constant: 0),
215+ fillInterval.rightAnchor.constraint(equalTo: endPoint.leftAnchor, constant: 0),
216+
217+ fillInterval.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
218+ fillInterval.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0)
219+ ])
220+
221+ let scrollViewPanStartGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanStart(recognizer:)))
222+ startPoint.addGestureRecognizer(scrollViewPanStartGesture)
223+
224+ let scrollViewPanEndGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanEnd(recognizer:)))
225+ endPoint.addGestureRecognizer(scrollViewPanEndGesture)
226+ }
227+
228+ func setUpReservedIntervals(_ reservations: [ReservationPeriod], _ isAdmin: Bool) {
229+
230+ for reservation in reservations {
231+ let uiview = UIView(frame: CGRect(x: 0, y: 0, width: Constants.widthTimePoint * reservation.duration, height: Constants.heightIntervals))
232+ uiview.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 0.5)
233+
234+
235+ uiview.translatesAutoresizingMaskIntoConstraints = false
236+ scrollView.addSubview(uiview)
237+
238+ guard let startDate = startDate else { return }
239+
240+ let s = reservation.startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
241+ let d = reservation.duration * Constants.widthTimePoint
242+
243+ let startTime = s < 0 ? 0 : s
244+
245+ let total = (isAdmin ? Constants.adminDuration : Constants.clientDuration) * Constants.widthTimePoint
246+ var duration = 0
247+ if s < 0 {
248+ duration = reservation.endTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
249+ } else {
250+ duration = (s + d) > total ? total - s : d
251+ }
252+
253+ NSLayoutConstraint.activate([
254+ uiview.bottomAnchor.constraint(equalToSystemSpacingBelow: scrollView.bottomAnchor, multiplier: 0),
255+ uiview.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
256+ constant: CGFloat(Constants.widthTimePoint / 2 + startTime)),
257+ uiview.widthAnchor.constraint(equalToConstant: CGFloat(duration)),
258+ uiview.heightAnchor.constraint(equalToConstant: CGFloat(Constants.heightIntervals))
259+ ])
260+ }
261+ }
262+
263+ // MARK: - Pan
264+
265+ @objc func handlePanStart(recognizer:UIPanGestureRecognizer) {
266+
267+ let translation = recognizer.translation(in: self.intervalsStackView)
268+ let duration = abs(startLeadingConstraint.constant + translation.x - endLeadingConstraint.constant)
269+
270+ if let view = recognizer.view, duration >= Constants.minBookingLength {
271+ startLeadingConstraint.constant += translation.x
272+
273+ changeTime(startLeadingConstraint, .start)
274+
275+ view.setNeedsUpdateConstraints()
276+ }
277+
278+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
279+ }
280+
281+ @objc func handlePanEnd(recognizer:UIPanGestureRecognizer) {
282+
283+ let translation = recognizer.translation(in: self.intervalsStackView)
284+ let duration = abs(startLeadingConstraint.constant - (endLeadingConstraint.constant + translation.x))
285+
286+ if let view = recognizer.view, duration >= Constants.minBookingLength {
287+ endLeadingConstraint.constant += translation.x
288+
289+ changeTime(endLeadingConstraint, .end)
290+ view.setNeedsUpdateConstraints()
291+ }
292+
293+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
294+ }
295+
296+ func changeTime(_ constraint: NSLayoutConstraint, _ period: Period) {
297+ guard let startDate = startDate, let d = data else { return }
298+
299+ let offset = Int(constraint.constant) % Constants.widthTimePoint
300+
301+ if offset == 0 {
302+ let p = Int(constraint.constant) / Constants.widthTimePoint
303+ let time = startDate.addPeriods(p)
304+
305+ switch period {
306+ case .start:
307+ data = ReservationData(establishment: d.establishment,
308+ startTime: time,
309+ endTime: d.endTime,
310+ numberOfGuests: d.numberOfGuests,
311+ reservedTable: d.reservedTable)
312+ case .end:
313+ data = ReservationData(establishment: d.establishment,
314+ startTime: d.startTime,
315+ endTime: time,
316+ numberOfGuests: d.numberOfGuests,
317+ reservedTable: d.reservedTable)
318+ }
319+
320+ updateDateAndTime(withData: data)
321+ }
322+ }
323+}
324+
325+// MARK: - Nested types
326+
327+extension AdminReservationView {
328+ enum Constants {
329+ static let widthTimePoint = 11
330+
331+ static let adminDuration = 288
332+ static let clientDuration = 90
333+ // 12:00 = 12 * 6
334+ static let clientStartingPoint = 72
335+
336+ static let pointWidth: CGFloat = 19
337+ static let pointHeight: CGFloat = 85
338+
339+ static let minBookingLength: CGFloat = 132
340+
341+ static let heightIntervals = 85
342+ }
343+
344+ enum Period {
345+ case start
346+ case end
347+ }
348+}
349+
350+
351+extension AdminReservationView: PickerViewUpdater {
352+
353+ func update(withModel model: IntervalPickerModel) {
354+ guard let d = data else { return }
355+
356+ data = ReservationData(establishment: d.establishment,
357+ startTime: model.startTime,
358+ endTime: model.endTime,
359+ numberOfGuests: d.numberOfGuests,
360+ reservedTable: d.reservedTable)
361+
362+ updateDateAndTime(withData: data)
363+ }
364+}
Hookah Tau/Controllers/Admin/Reservations/Views/AdminReservationView.xibnew file+208
@@ -0,0 +1,208 @@
1+<?xml version="1.0" encoding="UTF-8"?>
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="retina4_7" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <objects>
11+ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
12+ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
13+ <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="AdminReservationView" customModule="Hookah_Tau" customModuleProvider="target">
14+ <rect key="frame" x="0.0" y="0.0" width="414" height="441"/>
15+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
16+ <subviews>
17+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="48" translatesAutoresizingMaskIntoConstraints="NO" id="AEf-R4-K1F">
18+ <rect key="frame" x="0.0" y="16" width="414" height="404"/>
19+ <subviews>
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="143.5"/>
22+ <subviews>
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"/>
25+ <subviews>
26+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="hEV-kW-bBH">
27+ <rect key="frame" x="0.0" y="0.0" width="129" height="77.5"/>
28+ <subviews>
29+ <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">
30+ <rect key="frame" x="0.0" y="0.0" width="93" height="26.5"/>
31+ <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
32+ <nil key="textColor"/>
33+ <nil key="highlightedColor"/>
34+ </label>
35+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="29 марта" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KHM-Yj-bBa">
36+ <rect key="frame" x="0.0" y="31.5" width="78" height="20.5"/>
37+ <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
38+ <nil key="textColor"/>
39+ <nil key="highlightedColor"/>
40+ </label>
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"/>
43+ <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
44+ <nil key="textColor"/>
45+ <nil key="highlightedColor"/>
46+ </label>
47+ </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>
52+ </stackView>
53+ <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">
54+ <rect key="frame" x="0.0" y="99.5" width="109" height="37"/>
55+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
56+ <constraints>
57+ <constraint firstAttribute="width" constant="109" id="11r-IB-3r0"/>
58+ <constraint firstAttribute="height" constant="37" id="aej-Xt-jCJ"/>
59+ </constraints>
60+ <state key="normal" title="Button"/>
61+ </button>
62+ </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>
68+ </stackView>
69+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="bottom" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="59c-nG-5Fh">
70+ <rect key="frame" x="133" y="0.0" width="239" height="71.5"/>
71+ <subviews>
72+ <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">
73+ <rect key="frame" x="160.5" y="0.0" width="78.5" height="26.5"/>
74+ <fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
75+ <nil key="textColor"/>
76+ <nil key="highlightedColor"/>
77+ </label>
78+ <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">
79+ <rect key="frame" x="65" y="34.5" width="174" height="37"/>
80+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
81+ <constraints>
82+ <constraint firstAttribute="height" constant="37" id="7Wj-WF-xzD"/>
83+ <constraint firstAttribute="width" constant="174" id="ZYo-ko-rwn"/>
84+ </constraints>
85+ <state key="normal" title="Button"/>
86+ </button>
87+ </subviews>
88+ </stackView>
89+ </subviews>
90+ </stackView>
91+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="9RO-aK-yNh">
92+ <rect key="frame" x="21" y="191.5" width="372" height="58.5"/>
93+ <subviews>
94+ <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">
95+ <rect key="frame" x="0.0" y="0.0" width="372" height="20.5"/>
96+ <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
97+ <nil key="textColor"/>
98+ <nil key="highlightedColor"/>
99+ </label>
100+ <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="2" minValue="1" maxValue="8" translatesAutoresizingMaskIntoConstraints="NO" id="Lqw-9Z-34d">
101+ <rect key="frame" x="-2" y="28.5" width="376" height="31"/>
102+ <connections>
103+ <action selector="valueChanged:" destination="iN0-l3-epB" eventType="valueChanged" id="OwE-Nq-PHY"/>
104+ </connections>
105+ </slider>
106+ </subviews>
107+ </stackView>
108+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vKS-ia-RxW">
109+ <rect key="frame" x="0.0" y="298" width="414" height="106"/>
110+ <subviews>
111+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EiP-QO-PbU">
112+ <rect key="frame" x="0.0" y="20" width="414" height="85"/>
113+ <subviews>
114+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="OpX-io-LgH">
115+ <rect key="frame" x="0.0" y="0.0" width="414" height="1"/>
116+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
117+ <constraints>
118+ <constraint firstAttribute="height" constant="1" id="k2D-mj-31b"/>
119+ </constraints>
120+ </view>
121+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fOe-cM-riJ">
122+ <rect key="frame" x="0.0" y="84" width="414" height="1"/>
123+ <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
124+ <constraints>
125+ <constraint firstAttribute="height" constant="1" id="A2B-su-3Hi"/>
126+ </constraints>
127+ </view>
128+ </subviews>
129+ <color key="backgroundColor" red="0.8901960784313725" green="0.8901960784313725" blue="0.8901960784313725" alpha="0.6951519692" colorSpace="calibratedRGB"/>
130+ <constraints>
131+ <constraint firstItem="OpX-io-LgH" firstAttribute="top" secondItem="EiP-QO-PbU" secondAttribute="top" id="73d-6U-4Jc"/>
132+ <constraint firstAttribute="height" constant="85" id="JMc-tA-wHe"/>
133+ <constraint firstAttribute="trailing" secondItem="fOe-cM-riJ" secondAttribute="trailing" id="JVQ-ub-pEy"/>
134+ <constraint firstItem="OpX-io-LgH" firstAttribute="leading" secondItem="EiP-QO-PbU" secondAttribute="leading" id="hBd-hJ-yz7"/>
135+ <constraint firstAttribute="bottom" secondItem="fOe-cM-riJ" secondAttribute="bottom" id="im0-y1-nC8"/>
136+ <constraint firstItem="fOe-cM-riJ" firstAttribute="leading" secondItem="EiP-QO-PbU" secondAttribute="leading" id="qH9-Ww-nd5"/>
137+ <constraint firstAttribute="trailing" secondItem="OpX-io-LgH" secondAttribute="trailing" id="ueR-ZM-ELi"/>
138+ </constraints>
139+ </view>
140+ <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="trF-VM-0qV">
141+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
142+ <subviews>
143+ <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bpJ-pe-tQf">
144+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
145+ </stackView>
146+ </subviews>
147+ <constraints>
148+ <constraint firstAttribute="bottom" secondItem="bpJ-pe-tQf" secondAttribute="bottom" id="BxY-h0-LHi"/>
149+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="leading" secondItem="trF-VM-0qV" secondAttribute="leading" id="Ec5-Lb-NsX"/>
150+ <constraint firstAttribute="trailing" secondItem="bpJ-pe-tQf" secondAttribute="trailing" id="d0a-8z-CE6"/>
151+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="width" secondItem="trF-VM-0qV" secondAttribute="width" priority="250" id="mYw-sK-G8B"/>
152+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="height" secondItem="trF-VM-0qV" secondAttribute="height" id="wdB-oa-hvt"/>
153+ <constraint firstItem="bpJ-pe-tQf" firstAttribute="top" secondItem="trF-VM-0qV" secondAttribute="top" id="x6e-gw-nXA"/>
154+ </constraints>
155+ <viewLayoutGuide key="contentLayoutGuide" id="QAB-NG-eb4"/>
156+ <viewLayoutGuide key="frameLayoutGuide" id="xUW-w2-Jj1"/>
157+ </scrollView>
158+ </subviews>
159+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
160+ <constraints>
161+ <constraint firstAttribute="bottom" secondItem="trF-VM-0qV" secondAttribute="bottom" id="4kT-64-W5t"/>
162+ <constraint firstItem="trF-VM-0qV" firstAttribute="top" secondItem="vKS-ia-RxW" secondAttribute="top" id="M4X-6f-0WX"/>
163+ <constraint firstItem="trF-VM-0qV" firstAttribute="leading" secondItem="vKS-ia-RxW" secondAttribute="leading" id="ZXK-Vm-o6S"/>
164+ <constraint firstAttribute="trailing" secondItem="trF-VM-0qV" secondAttribute="trailing" id="a2R-kF-NcS"/>
165+ <constraint firstAttribute="height" constant="106" id="ebp-l7-k9Z"/>
166+ <constraint firstItem="EiP-QO-PbU" firstAttribute="leading" secondItem="vKS-ia-RxW" secondAttribute="leading" id="fHc-26-5lu"/>
167+ <constraint firstAttribute="bottom" secondItem="EiP-QO-PbU" secondAttribute="bottom" constant="1" id="kxj-VY-srA"/>
168+ <constraint firstAttribute="trailing" secondItem="EiP-QO-PbU" secondAttribute="trailing" id="sbQ-sE-GAh"/>
169+ </constraints>
170+ </view>
171+ </subviews>
172+ <constraints>
173+ <constraint firstItem="vKS-ia-RxW" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" id="0cO-AV-PJL"/>
174+ <constraint firstAttribute="trailing" secondItem="oh7-ka-ctS" secondAttribute="trailing" constant="21" id="KeE-6F-h5x"/>
175+ <constraint firstAttribute="trailing" secondItem="9RO-aK-yNh" secondAttribute="trailing" constant="21" id="Kh6-4l-QJO"/>
176+ <constraint firstAttribute="bottom" secondItem="vKS-ia-RxW" secondAttribute="bottom" id="TKW-xo-b61"/>
177+ <constraint firstItem="9RO-aK-yNh" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" constant="21" id="Teg-kp-nZt"/>
178+ <constraint firstItem="oh7-ka-ctS" firstAttribute="leading" secondItem="AEf-R4-K1F" secondAttribute="leading" constant="21" id="mJ2-gh-vCg"/>
179+ <constraint firstAttribute="trailing" secondItem="vKS-ia-RxW" secondAttribute="trailing" id="uDV-uM-jby"/>
180+ </constraints>
181+ </stackView>
182+ </subviews>
183+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
184+ <constraints>
185+ <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="AEf-R4-K1F" secondAttribute="bottom" constant="21" id="4MN-Yl-hYv"/>
186+ <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="AEf-R4-K1F" secondAttribute="trailing" id="C1h-bA-S6p"/>
187+ <constraint firstItem="AEf-R4-K1F" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="H1o-ua-Tnn"/>
188+ <constraint firstItem="vUN-kp-3ea" firstAttribute="top" secondItem="AEf-R4-K1F" secondAttribute="top" constant="-16" id="eYI-is-YLt"/>
189+ </constraints>
190+ <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
191+ <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
192+ <connections>
193+ <outlet property="callButton" destination="Xkd-xQ-cFd" id="B3w-gf-Yqg"/>
194+ <outlet property="cancelButton" destination="iiD-uf-IDp" id="zwd-eJ-GsP"/>
195+ <outlet property="dateLabel" destination="KHM-Yj-bBa" id="iS8-eN-OFW"/>
196+ <outlet property="guestSlider" destination="Lqw-9Z-34d" id="cEj-JF-J4D"/>
197+ <outlet property="intervalsContainerView" destination="vKS-ia-RxW" id="0Ig-rh-hXD"/>
198+ <outlet property="intervalsStackView" destination="bpJ-pe-tQf" id="aF0-Qm-kuh"/>
199+ <outlet property="nameLabel" destination="OoG-F4-OVu" id="rnW-fG-jFs"/>
200+ <outlet property="numberOfGuestLabel" destination="yTM-U5-XjI" id="z0O-94-Mp7"/>
201+ <outlet property="scrollView" destination="trF-VM-0qV" id="P5v-fg-hUW"/>
202+ <outlet property="tableLabel" destination="qNR-EO-oMK" id="63P-AS-fgf"/>
203+ <outlet property="timeLabel" destination="gau-aS-vtT" id="7Iy-Cz-zdY"/>
204+ </connections>
205+ <point key="canvasLocation" x="137.59999999999999" y="120.98950524737631"/>
206+ </view>
207+ </objects>
208+</document>
Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.swiftnew file+22
@@ -0,0 +1,22 @@
1+//
2+// AdminNoReservationsView.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 AdminNoReservationsView: UIView {
12+
13+ // MARK: - IBAOutlets
14+
15+ @IBOutlet weak var bookButton: Button? {
16+ didSet {
17+ let style = BlackButtonStyle()
18+ style.apply(to: bookButton, withTitle: "ЗАБРОНИРОВАТЬ")
19+ }
20+ }
21+
22+}
Hookah Tau/Controllers/Admin/Reservations/Views/No reserv/AdminNoReservationsView.xibnew file+53
@@ -0,0 +1,53 @@
1+<?xml version="1.0" encoding="UTF-8"?>
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="retina6_1" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <objects>
11+ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
12+ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
13+ <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="AdminNoReservationsView" customModule="Hookah_Tau" customModuleProvider="target">
14+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
15+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
16+ <subviews>
17+ <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="46" translatesAutoresizingMaskIntoConstraints="NO" id="Ytd-08-vB9">
18+ <rect key="frame" x="107" y="382" width="200" height="142"/>
19+ <subviews>
20+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="В вашем заведении пока не забронирован ни один столик :(" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Svd-DB-3Zu">
21+ <rect key="frame" x="4" y="0.0" width="192" height="61"/>
22+ <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
23+ <nil key="textColor"/>
24+ <nil key="highlightedColor"/>
25+ </label>
26+ <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="y3F-ua-mLb" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
27+ <rect key="frame" x="12.5" y="107" width="175" height="35"/>
28+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
29+ <constraints>
30+ <constraint firstAttribute="width" constant="175" id="LnY-Iu-4e6"/>
31+ <constraint firstAttribute="height" constant="35" id="oN3-oc-fHx"/>
32+ </constraints>
33+ <state key="normal" title="Button"/>
34+ </button>
35+ </subviews>
36+ <constraints>
37+ <constraint firstAttribute="width" constant="200" id="pgG-jg-faG"/>
38+ </constraints>
39+ </stackView>
40+ </subviews>
41+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
42+ <constraints>
43+ <constraint firstItem="Ytd-08-vB9" firstAttribute="centerY" secondItem="vUN-kp-3ea" secondAttribute="centerY" id="ewL-z8-YVD"/>
44+ <constraint firstItem="Ytd-08-vB9" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="txy-j9-mF1"/>
45+ </constraints>
46+ <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
47+ <connections>
48+ <outlet property="bookButton" destination="y3F-ua-mLb" id="aC4-gz-Gqd"/>
49+ </connections>
50+ <point key="canvasLocation" x="137.68115942028987" y="91.741071428571431"/>
51+ </view>
52+ </objects>
53+</document>
Hookah Tau/Controllers/Admin/Tab bar/Admin.storyboardnew file+79
@@ -0,0 +1,79 @@
1+<?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">
3+ <device id="retina6_1" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <scenes>
11+ <!--Navigation Controller-->
12+ <scene sceneID="Q3i-Bo-7ns">
13+ <objects>
14+ <viewController id="Ifn-72-mgx" customClass="UINavigationController" sceneMemberID="viewController">
15+ <view key="view" contentMode="scaleToFill" id="R2t-qa-IDx">
16+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
17+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
19+ <viewLayoutGuide key="safeArea" id="yHG-bf-ndj"/>
20+ </view>
21+ <tabBarItem key="tabBarItem" systemItem="downloads" id="9K1-uc-72H"/>
22+ </viewController>
23+ <placeholder placeholderIdentifier="IBFirstResponder" id="8X9-ca-bCi" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
24+ </objects>
25+ <point key="canvasLocation" x="1061" y="-327"/>
26+ </scene>
27+ <!--Navigation Controller-->
28+ <scene sceneID="cAb-sw-24k">
29+ <objects>
30+ <viewController id="Jsm-cH-XbJ" customClass="UINavigationController" sceneMemberID="viewController">
31+ <view key="view" contentMode="scaleToFill" id="s24-jW-vhb">
32+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
33+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
34+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
35+ <viewLayoutGuide key="safeArea" id="f96-UV-eoi"/>
36+ </view>
37+ <tabBarItem key="tabBarItem" systemItem="contacts" id="ziF-T0-lg1"/>
38+ </viewController>
39+ <placeholder placeholderIdentifier="IBFirstResponder" id="qqi-vY-Mog" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
40+ </objects>
41+ <point key="canvasLocation" x="1061" y="433"/>
42+ </scene>
43+ <!--Navigation Controller-->
44+ <scene sceneID="p6D-Gs-DUK">
45+ <objects>
46+ <viewController id="Tc3-UW-Fwi" customClass="UINavigationController" sceneMemberID="viewController">
47+ <view key="view" contentMode="scaleToFill" id="dmc-GR-czf">
48+ <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
49+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
50+ <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
51+ <viewLayoutGuide key="safeArea" id="z8c-Ub-USh"/>
52+ </view>
53+ <tabBarItem key="tabBarItem" systemItem="mostViewed" id="szW-oN-ixR"/>
54+ </viewController>
55+ <placeholder placeholderIdentifier="IBFirstResponder" id="1ST-zr-4Zs" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
56+ </objects>
57+ <point key="canvasLocation" x="1062" y="-1047"/>
58+ </scene>
59+ <!--Admin Tabbar Controller-->
60+ <scene sceneID="cWI-V1-4ZB">
61+ <objects>
62+ <tabBarController storyboardIdentifier="AdminTabbarController" id="mgG-ir-mFF" customClass="AdminTabbarController" customModule="Hookah_Tau" customModuleProvider="target" sceneMemberID="viewController">
63+ <tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="src-m7-O6h">
64+ <rect key="frame" x="0.0" y="0.0" width="414" height="49"/>
65+ <autoresizingMask key="autoresizingMask"/>
66+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
67+ </tabBar>
68+ <connections>
69+ <segue destination="Tc3-UW-Fwi" kind="relationship" relationship="viewControllers" id="K4a-6A-WdW"/>
70+ <segue destination="Ifn-72-mgx" kind="relationship" relationship="viewControllers" id="NdV-rq-bli"/>
71+ <segue destination="Jsm-cH-XbJ" kind="relationship" relationship="viewControllers" id="2PQ-04-zRb"/>
72+ </connections>
73+ </tabBarController>
74+ <placeholder placeholderIdentifier="IBFirstResponder" id="oiC-tc-RNa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
75+ </objects>
76+ <point key="canvasLocation" x="-817" y="-251"/>
77+ </scene>
78+ </scenes>
79+</document>
Hookah Tau/Controllers/Admin/Tab bar/AdminTabbarController.swiftnew file+48
@@ -0,0 +1,48 @@
1+//
2+// AdminTabbarController.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 05/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+class AdminTabbarController: UITabBarController {
12+
13+ // MARK: - Properties
14+
15+ var onViewDidLoad: TabbarClosure?
16+ var onReservationsFlow: TabbarClosure?
17+ var onClientsFlow: TabbarClosure?
18+ var onProfileFlow: TabbarClosure?
19+
20+ // MARK: - Lifecycle
21+
22+ override func viewDidLoad() {
23+ super.viewDidLoad()
24+
25+ delegate = self
26+ if let controller = customizableViewControllers?.first as? UINavigationController {
27+ onViewDidLoad?(controller)
28+ }
29+ }
30+
31+ func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)
32+ {
33+
34+ guard let controller = viewControllers?[selectedIndex] as? UINavigationController else { return }
35+
36+ switch selectedIndex {
37+ case 0:
38+ onReservationsFlow?(controller)
39+ case 1:
40+ onClientsFlow?(controller)
41+ default:
42+ onProfileFlow?(controller)
43+ }
44+ }
45+
46+}
47+
48+extension AdminTabbarController: UITabBarControllerDelegate {}
Hookah Tau/Controllers/Admin/Tab bar/AdminTabbarCoordinator.swiftnew file+73
@@ -0,0 +1,73 @@
1+//
2+// AdminTabbarCoordinator.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 05/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import UIKit
10+
11+
12+class AdminTabbarCoordinator: BaseCoordinator {
13+
14+ /// Мы тут определяем что происходит с таббарами
15+ override func start() {
16+ let storyboard = UIStoryboard(name: "Admin", bundle: nil)
17+ let tabbarView = storyboard.instantiateViewController(withIdentifier: "AdminTabbarController") as? AdminTabbarController
18+
19+ tabbarView?.onViewDidLoad = runReservationsFlow()
20+ tabbarView?.onClientsFlow = runClientsFlow()
21+ tabbarView?.onProfileFlow = runProfileFlow()
22+ tabbarView?.onReservationsFlow = runReservationsFlow()
23+
24+ let appDelegate = UIApplication.shared.delegate as? AppDelegate
25+ guard let appD = appDelegate else { return }
26+ appD.window?.rootViewController = tabbarView
27+ }
28+
29+ private func runReservationsFlow() -> TabbarClosure {
30+ return { [unowned self] navigationController in
31+ if navigationController.viewControllers.isEmpty {
32+ let addressCoordinator = AdminAddressCoordinator(navigationController: navigationController)
33+ self.addDependency(addressCoordinator)
34+
35+ addressCoordinator.didEndFlow = {
36+ self.removeDependency(addressCoordinator)
37+ }
38+
39+ addressCoordinator.start()
40+ }
41+ }
42+ }
43+
44+ private func runClientsFlow() -> TabbarClosure {
45+ return { [unowned self] navigationController in
46+ if navigationController.viewControllers.isEmpty {
47+ let clientsCoordinator = ClientsCoordinator(navigationController: navigationController)
48+ self.addDependency(clientsCoordinator)
49+
50+ clientsCoordinator.didEndFlow = {
51+ self.removeDependency(clientsCoordinator)
52+ }
53+
54+ clientsCoordinator.start()
55+ }
56+ }
57+ }
58+
59+ private func runProfileFlow() -> TabbarClosure {
60+ return { [unowned self] navigationController in
61+ if navigationController.viewControllers.isEmpty {
62+ let profileCoordinator = ProfileCoordinator(navigationController: navigationController)
63+ profileCoordinator.didEndFlow = { [weak self, weak profileCoordinator] in
64+ self?.removeDependency(profileCoordinator)
65+ self?.didEndFlow?()
66+ }
67+ self.addDependency(profileCoordinator)
68+ profileCoordinator.start()
69+ }
70+ }
71+ }
72+
73+}
Hookah Tau/Controllers/ApplicationCoordinator.swift+8−1
@@ -42,7 +42,13 @@ class ApplicationCoordinator: BaseCoordinator {
4242 }
4343
4444 private func runAdminFlow() {
45−
45+ let tabbarCoordinator = AdminTabbarCoordinator(navigationController: navigationController)
46+ tabbarCoordinator.didEndFlow = { [weak self, weak tabbarCoordinator] in
47+ self?.start()
48+ self?.removeDependency(tabbarCoordinator)
49+ }
50+ addDependency(tabbarCoordinator)
51+ tabbarCoordinator.start()
4652 }
4753
4854 private func runClientFlow() {
@@ -66,6 +72,7 @@ extension ApplicationCoordinator {
6672 let isAdmin = DataStorage.standard.getUserModel()?.isAdmin ?? false
6773
6874 if !isAuthorized {
75+ DataStorage.standard.setLoggedInState(false)
6976 return .auth
7077 }
7178
Hookah Tau/Controllers/BaseViewController.swift+1−1
@@ -111,7 +111,7 @@ class BaseViewController: UIViewController {
111111 // MARK: - Alert
112112
113113 func displayAlert(forError error: GeneralError = GeneralError.noData, with message: String? = nil) {
114− var title: String? = message != nil ? error.localizedDescription : message
114+ var title: String? = message == nil ? error.localizedDescription : message
115115 switch error {
116116 case .serverError(let se):
117117 title = se.error
Hookah Tau/Controllers/Client/Profile/ProfileChangeCoordinator.swift+6−4
@@ -29,10 +29,12 @@ class ProfileChangeCoordinator: BaseCoordinator {
2929 }
3030
3131 /// Done
32− func update(withModel model: User) {
33− for controller in navigationController?.viewControllers ?? [] {
34− guard let updatableController = controller as? UserUpdate else { continue }
35− updatableController.updateUser(withModel: model)
32+ func update(withModel model: User?) {
33+ if let model = model {
34+ for controller in navigationController?.viewControllers ?? [] {
35+ guard let updatableController = controller as? UserUpdate else { continue }
36+ updatableController.updateUser(withModel: model)
37+ }
3638 }
3739
3840 didEndFlow?()
Hookah Tau/Controllers/Client/Profile/ProfileChangeViewController.swift+22−3
@@ -23,6 +23,8 @@ class ProfileChangeViewController: BaseViewController {
2323
2424 var changeView: ChangeView?
2525
26+ var settingsService: SettingsService?
27+
2628 var contentView: UIView = {
2729 let view = UIView()
2830 view.translatesAutoresizingMaskIntoConstraints = false
@@ -57,6 +59,8 @@ class ProfileChangeViewController: BaseViewController {
5759
5860 setUpView()
5961 setUpKeyboard()
62+
63+ settingsService = SettingsService(apiClient: APIClient.shared)
6064 }
6165
6266 func setUpView() {
@@ -86,15 +90,30 @@ class ProfileChangeViewController: BaseViewController {
8690 var userModel = model.userModel
8791 switch model.editingOption {
8892 case .invite:
89− print("New person is added")
90− return
93+ addNewAdmin(phone: text.toApiPhoneNumberFormat())
9194 case .name:
95+ // TODO:-f
9296 userModel.name = text
97+ coordinator?.update(withModel: userModel)
9398 case .phone:
99+ // TODO:-
94100 userModel.phoneNumber = text
101+ coordinator?.update(withModel: userModel)
95102 }
96103
97− coordinator?.update(withModel: userModel)
98104 }
99105
106+ func addNewAdmin(phone: String) {
107+ doneButton.loading = true
108+
109+ settingsService?.addOtherAdmin(phone: phone) { [weak self] res in
110+ if res {
111+ self?.coordinator?.update(withModel: nil)
112+ } else {
113+ self?.displayAlert(with: "Что-то пошло не так!\nПопробуй еще раз")
114+ }
115+
116+ self?.doneButton.loading = false
117+ }
118+ }
100119}
Hookah Tau/Controllers/Client/Profile/Views/ChangeView.swift+32−1
@@ -11,15 +11,20 @@ import UIKit
1111
1212class ChangeView: UIView {
1313
14+ var type: EditingOption = .invite
15+
1416 @IBOutlet weak var titleLabel: UILabel?
1517
1618 @IBOutlet weak var textField: UITextField?
1719
1820 func bind(withModel model: ChangeModel) {
21+ self.textField?.delegate = self
22+ self.type = model.editingOption
23+
1924 switch model.editingOption {
2025 case .invite:
2126 titleLabel?.text = "Добавить администратора"
22− textField?.placeholder = "+ 7 888 888 88 88"
27+ textField?.placeholder = "+ 7 (888) 888-88-88"
2328 textField?.keyboardType = .numberPad
2429 case .name:
2530 titleLabel?.text = "Имя"
@@ -32,3 +37,29 @@ class ChangeView: UIView {
3237 }
3338 }
3439}
40+
41+extension ChangeView: UITextFieldDelegate {
42+
43+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
44+ let newString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
45+ textField.text = formattedNumber(newString)
46+ return false
47+ }
48+
49+ private func formattedNumber(_ number: String) -> String {
50+ let cleanPhoneNumber = number.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
51+
52+ var result = ""
53+ let phoneMask = "+X (XXX) XXX XX XX"
54+ var index = cleanPhoneNumber.startIndex
55+ for ch in phoneMask where index < cleanPhoneNumber.endIndex {
56+ if ch == "X" {
57+ result.append(cleanPhoneNumber[index])
58+ index = cleanPhoneNumber.index(after: index)
59+ } else {
60+ result.append(ch)
61+ }
62+ }
63+ return result
64+ }
65+}
Hookah Tau/Controllers/Client/Profile/Views/ChangeView.xib+1−1
@@ -17,7 +17,7 @@
1717 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="19Q-ZO-gm4">
1818 <rect key="frame" x="20" y="11" width="335" height="83.5"/>
1919 <subviews>
20− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Имя" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oB7-uQ-xmw">
20+ <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="oB7-uQ-xmw">
2121 <rect key="frame" x="0.0" y="0.0" width="335" height="33.5"/>
2222 <fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
2323 <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
Hookah Tau/Controllers/Client/Profile/Views/ProfileView.xib+5−5
@@ -28,7 +28,7 @@
2828 </label>
2929 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jpS-Vl-9C3">
3030 <rect key="frame" x="0.0" y="44" width="374" height="50"/>
31− <color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
31+ <color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
3232 <constraints>
3333 <constraint firstAttribute="width" constant="374" id="9z3-UR-zHl"/>
3434 <constraint firstAttribute="height" constant="50" id="Hr9-hL-I1c"/>
@@ -47,7 +47,7 @@
4747 </label>
4848 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SVP-b8-AJi">
4949 <rect key="frame" x="0.0" y="41.5" width="374" height="50"/>
50− <color key="backgroundColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
50+ <color key="backgroundColor" systemColor="systemRedColor" red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
5151 <constraints>
5252 <constraint firstAttribute="width" constant="374" id="2Ra-kt-T1F"/>
5353 <constraint firstAttribute="height" constant="50" id="iGE-m0-EQ2"/>
@@ -58,7 +58,7 @@
5858 <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="wcw-Ss-ld4">
5959 <rect key="frame" x="0.0" y="245.5" width="374" height="91.5"/>
6060 <subviews>
61− <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NQB-i0-WVL">
61+ <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NQB-i0-WVL">
6262 <rect key="frame" x="0.0" y="0.0" width="71.5" height="33.5"/>
6363 <fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
6464 <color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -66,7 +66,7 @@
6666 </label>
6767 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dyQ-12-OMI">
6868 <rect key="frame" x="0.0" y="41.5" width="374" height="50"/>
69− <color key="backgroundColor" systemColor="systemIndigoColor" red="0.34509803919999998" green="0.33725490200000002" blue="0.83921568629999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
69+ <color key="backgroundColor" systemColor="systemIndigoColor" red="0.34509803921568627" green="0.33725490196078434" blue="0.83921568627450982" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
7070 <constraints>
7171 <constraint firstAttribute="height" constant="50" id="SYN-47-LUA"/>
7272 <constraint firstAttribute="width" constant="374" id="hQD-KD-teb"/>
@@ -78,7 +78,7 @@
7878 </stackView>
7979 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oKK-WM-xZ3" customClass="Button" customModule="Hookah_Tau" customModuleProvider="target">
8080 <rect key="frame" x="20" y="446" width="374" height="37"/>
81− <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
81+ <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058823529413" green="0.77647058823529413" blue="0.78431372549019607" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
8282 <constraints>
8383 <constraint firstAttribute="height" constant="37" id="I8c-3h-mMu"/>
8484 </constraints>
Hookah Tau/Controllers/Client/Reservations/Active Reservations/ReservationsViewController.swift−2
@@ -8,8 +8,6 @@
88
99import UIKit
1010
11−let reservationCell = "ReservationCell"
12−
1311class ReservationsViewController: BaseViewController {
1412
1513 // MARK: - Properties
Hookah Tau/Controllers/Client/Reservations/Adress of reservation/AddresViewController.swift+2−2
@@ -12,7 +12,7 @@ class AddresViewController: BaseViewController {
1212
1313 // MARK: - Properties
1414
15− weak var coordinator: AddressCoordinator?
15+ weak var coordinator: AddressMapper?
1616
1717 var tableView: UITableView?
1818
@@ -78,7 +78,7 @@ extension AddresViewController: UITableViewDelegate, UITableViewDataSource {
7878 let establishment = dataSource?[indexPath.row]
7979 guard let id = establishment else { return }
8080
81− coordinator?.chooseTableAndTime(inEstablishment: id)
81+ coordinator?.chooseAddress(establishmentId: id)
8282 }
8383
8484 func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
Hookah Tau/Controllers/Client/Reservations/Adress of reservation/AddressCoordinator.swift+11−1
@@ -8,6 +8,10 @@
88
99import UIKit
1010
11+protocol AddressMapper: class {
12+ func chooseAddress(establishmentId id: Int)
13+}
14+
1115class AddressCoordinator: BaseCoordinator {
1216
1317 // MARK: - Properties
@@ -29,7 +33,7 @@ class AddressCoordinator: BaseCoordinator {
2933
3034 /// continue with reservation
3135 /// TODO: pass some data
32− func chooseTableAndTime(inEstablishment id: Int) {
36+ private func chooseTableAndTime(inEstablishment id: Int) {
3337 let reservationCoordinator = FirstStepReservationCoordinator(navigationController: navigationController)
3438 reservationCoordinator.establishment = id
3539
@@ -43,3 +47,9 @@ class AddressCoordinator: BaseCoordinator {
4347 reservationCoordinator.start()
4448 }
4549}
50+
51+extension AddressCoordinator: AddressMapper {
52+ func chooseAddress(establishmentId id: Int) {
53+ chooseTableAndTime(inEstablishment: id)
54+ }
55+}
Hookah Tau/Controllers/Client/Reservations/Cells/Reservation/ReservationCell.swift+2
@@ -8,6 +8,8 @@
88
99import UIKit
1010
11+let reservationCell = "ReservationCell"
12+
1113class ReservationCell: UITableViewCell {
1214
1315 // MARK: - Properties
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/Steps/2/SecondStepReservationViewController.swift+4−4
@@ -84,7 +84,7 @@ class SecondStepReservationViewController: BaseViewController {
8484 view.addSubview(map)
8585
8686 NSLayoutConstraint.activate([
87− map.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
87+ map.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
8888 map.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
8989 map.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
9090 map.bottomAnchor.constraint(equalTo: childContentView.topAnchor, constant: 0)
@@ -92,10 +92,10 @@ class SecondStepReservationViewController: BaseViewController {
9292
9393 let width = view.frame.width
9494
95− mapView?.scrollViewParent.setContentOffset(CGPoint(x: 0, y: -100), animated: false)
96− mapView?.scrollViewParent.contentInset = UIEdgeInsets(top: 100,
95+ mapView?.scrollViewParent.setContentOffset(CGPoint(x: 0, y: 100), animated: false)
96+ mapView?.scrollViewParent.contentInset = UIEdgeInsets(top: 0,
9797 left: width / 2,
98− bottom: 100,
98+ bottom: -100,
9999 right: width / 2)
100100 mapView?.isUserInteractionEnabled = false
101101 view.sendSubviewToBack(map)
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/ClientTabbarController.swift+6−6
@@ -10,18 +10,18 @@ import Foundation
1010import UIKit
1111
1212protocol TabbarView: class {
13− var onReservationFlowSelect: ((UINavigationController) -> ())? { get set }
14− var onProfileFlowSelect: ((UINavigationController) -> ())? { get set }
15− var onViewDidLoad: ((UINavigationController) -> ())? { get set }
13+ var onReservationFlowSelect: TabbarClosure? { get set }
14+ var onProfileFlowSelect: TabbarClosure? { get set }
15+ var onViewDidLoad: TabbarClosure? { get set }
1616}
1717
1818final class ClientTabbarController: UITabBarController, UITabBarControllerDelegate, TabbarView {
1919
2020 // MARK: - Properties
2121
22− var onReservationFlowSelect: ((UINavigationController) -> ())?
23− var onProfileFlowSelect: ((UINavigationController) -> ())?
24− var onViewDidLoad: ((UINavigationController) -> ())?
22+ var onReservationFlowSelect: TabbarClosure?
23+ var onProfileFlowSelect: TabbarClosure?
24+ var onViewDidLoad: TabbarClosure?
2525
2626
2727 // MARK: - Lifecycle
Hookah Tau/Controllers/Client/Tab bar/ClientTabbarCoordinator.swift+3−3
@@ -8,10 +8,10 @@
88
99import UIKit
1010
11+typealias TabbarClosure = ((UINavigationController) -> ())
12+
1113class ClientTabbarCoordinator: BaseCoordinator {
1214
13− typealias TabbarClosure = ((UINavigationController) -> ())
14−
1515 // MARK: - Lifecycle
1616
1717 override func start() {
@@ -37,7 +37,7 @@ class ClientTabbarCoordinator: BaseCoordinator {
3737 }
3838 }
3939
40− private func runProfileFlow() -> TabbarClosure{
40+ private func runProfileFlow() -> TabbarClosure {
4141 return { navigationController in
4242 if navigationController.viewControllers.isEmpty {
4343 let profileCoordinator = ProfileCoordinator(navigationController: navigationController)
Hookah Tau/Controllers/Picker view/IntervalPickerView.swiftnew file+265
@@ -0,0 +1,265 @@
1+//
2+// IntervalPickerView.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+struct IntervalPickerModel {
12+ var startTime: Date
13+ var endTime: Date
14+}
15+
16+protocol PickerViewUpdater: class {
17+ func update(withModel model: IntervalPickerModel)
18+}
19+
20+class IntervalPickerView: UIView {
21+
22+ // MARK: - IBAOutlets
23+
24+ @IBOutlet weak var scrollView: UIScrollView!
25+
26+ @IBOutlet weak var intervalsStackView: UIStackView!
27+
28+ // MARK: - Properties
29+
30+ // констрейнты начала и конца интервала
31+ var startLeadingConstraint: NSLayoutConstraint!
32+ var endLeadingConstraint: NSLayoutConstraint!
33+
34+ var startPoint: UIImageView = {
35+ var view = UIImageView()
36+ view.isUserInteractionEnabled = true
37+ view.image = #imageLiteral(resourceName: "handle_left")
38+ view.translatesAutoresizingMaskIntoConstraints = false
39+ return view
40+ }()
41+
42+ var endPoint: UIImageView = {
43+ var view = UIImageView()
44+ view.isUserInteractionEnabled = true
45+ view.image = #imageLiteral(resourceName: "Group")
46+ view.translatesAutoresizingMaskIntoConstraints = false
47+ return view
48+ }()
49+
50+ /// Интервал
51+ var fillInterval: UIView = {
52+ var view = UIView()
53+ view.backgroundColor = #colorLiteral(red: 0, green: 0.5921568627, blue: 1, alpha: 0.3426262843)
54+ view.translatesAutoresizingMaskIntoConstraints = false
55+ return view
56+ }()
57+
58+ /// Время начала вертелки.
59+ /// Для клиента это 12:00 смены (надо вызвать метод для получения текущей смены).
60+ /// Для администратора это `t` - 24h
61+ var startDate: Date?
62+
63+ /// Моделька для смены промежутков
64+ var model: IntervalPickerModel? {
65+ didSet {
66+ guard let model = model else { return }
67+ updater?.update(withModel: model)
68+ }
69+ }
70+
71+ weak var updater: PickerViewUpdater?
72+
73+ // MARK: - bind
74+
75+ func bind(withModel model: ReservationData, user isAdmin: Bool, intervals: [ReservationPeriod]) {
76+ // isAdmin
77+ let isAdmin = DataStorage.standard.getUserModel()?.isAdmin ?? false
78+
79+ let (start, _) = isAdmin ? model.startTime.getAdminStartingAndEndPoint() : model.startTime.getClientStartingAndEndPoint()
80+
81+ startDate = start
82+
83+ setUpIntervals(isAdmin)
84+ setUpReservedIntervals(intervals, isAdmin)
85+ setUpBookingPeriod(model.startTime, model.endTime)
86+ }
87+
88+ /// Тут происходит установка интервалов. Разное для клиента и админа
89+ func setUpIntervals(_ isAdmin: Bool) {
90+ guard let startPoint = startDate else { return }
91+
92+ let startingPoint = isAdmin ? startPoint.getAdminsStartingPoint() : Constants.clientStartingPoint
93+ let endingPoint = startingPoint + (isAdmin ? Constants.adminDuration : Constants.clientDuration)
94+
95+ for period in startingPoint...endingPoint {
96+ let timePointView = TimePointView.loadFromNib()
97+ timePointView?.setUp(withTimePoint: period)
98+ let view = UIView(frame: CGRect(x: 0, y: 0, width: 35, height: 106))
99+ view.addSubviewThatFills(timePointView)
100+ intervalsStackView.addArrangedSubview(view)
101+ }
102+ }
103+
104+ private func setUpBookingPeriod(_ startTime: Date, _ endTime: Date? = nil) {
105+ self.scrollView.addSubview(startPoint)
106+ self.scrollView.addSubview(endPoint)
107+ self.scrollView.addSubview(fillInterval)
108+
109+ guard let startDate = startDate else { return }
110+
111+ let startConstr = CGFloat(startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
112+
113+ scrollView.setContentOffset(CGPoint(x: startConstr - 50, y: 0), animated: true)
114+
115+ startLeadingConstraint = startPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
116+ constant: startConstr)
117+
118+ if let end = endTime {
119+ let endConstr = CGFloat(end.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint)
120+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
121+ constant: endConstr)
122+ } else {
123+ endLeadingConstraint = endPoint.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
124+ constant:startConstr + Constants.minBookingLength)
125+ }
126+
127+ NSLayoutConstraint.activate([
128+
129+ startPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
130+ endPoint.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0),
131+
132+ startPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
133+ endPoint.widthAnchor.constraint(equalToConstant: Constants.pointWidth),
134+
135+ startPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
136+ endPoint.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
137+
138+ startLeadingConstraint,
139+ endLeadingConstraint,
140+
141+ fillInterval.leftAnchor.constraint(equalTo: startPoint.rightAnchor, constant: 0),
142+ fillInterval.rightAnchor.constraint(equalTo: endPoint.leftAnchor, constant: 0),
143+
144+ fillInterval.heightAnchor.constraint(equalToConstant: Constants.pointHeight),
145+ fillInterval.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0)
146+ ])
147+
148+ let scrollViewPanStartGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanStart(recognizer:)))
149+ startPoint.addGestureRecognizer(scrollViewPanStartGesture)
150+
151+ let scrollViewPanEndGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanEnd(recognizer:)))
152+ endPoint.addGestureRecognizer(scrollViewPanEndGesture)
153+ }
154+
155+ func setUpReservedIntervals(_ reservations: [ReservationPeriod], _ isAdmin: Bool) {
156+
157+ for reservation in reservations {
158+ let uiview = UIView(frame: CGRect(x: 0, y: 0, width: Constants.widthTimePoint * reservation.duration, height: Constants.heightIntervals))
159+ uiview.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 0.5)
160+
161+
162+ uiview.translatesAutoresizingMaskIntoConstraints = false
163+ scrollView.addSubview(uiview)
164+
165+ guard let startDate = startDate else { return }
166+
167+ let s = reservation.startTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
168+ let d = reservation.duration * Constants.widthTimePoint
169+
170+ let startTime = s < 0 ? 0 : s
171+
172+ let total = (isAdmin ? Constants.adminDuration : Constants.clientDuration) * Constants.widthTimePoint
173+ var duration = 0
174+ if s < 0 {
175+ duration = reservation.endTime.getMinutesPeriods(fromStart: startDate) * Constants.widthTimePoint
176+ } else {
177+ duration = (s + d) > total ? total - s : d
178+ }
179+
180+ NSLayoutConstraint.activate([
181+ uiview.bottomAnchor.constraint(equalToSystemSpacingBelow: scrollView.bottomAnchor, multiplier: 0),
182+ uiview.leftAnchor.constraint(equalTo: scrollView.leftAnchor,
183+ constant: CGFloat(Constants.widthTimePoint / 2 + startTime)),
184+ uiview.widthAnchor.constraint(equalToConstant: CGFloat(duration)),
185+ uiview.heightAnchor.constraint(equalToConstant: CGFloat(Constants.heightIntervals))
186+ ])
187+ }
188+ }
189+
190+ // MARK: - Pan
191+
192+ @objc func handlePanStart(recognizer:UIPanGestureRecognizer) {
193+
194+ let translation = recognizer.translation(in: self.intervalsStackView)
195+ let duration = abs(startLeadingConstraint.constant + translation.x - endLeadingConstraint.constant)
196+
197+ if let view = recognizer.view, duration >= Constants.minBookingLength {
198+ startLeadingConstraint.constant += translation.x
199+
200+ changeTime(startLeadingConstraint, .start)
201+
202+ view.setNeedsUpdateConstraints()
203+ }
204+
205+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
206+ }
207+
208+ @objc func handlePanEnd(recognizer:UIPanGestureRecognizer) {
209+
210+ let translation = recognizer.translation(in: self.intervalsStackView)
211+ let duration = abs(startLeadingConstraint.constant - (endLeadingConstraint.constant + translation.x))
212+
213+ if let view = recognizer.view, duration >= Constants.minBookingLength {
214+ endLeadingConstraint.constant += translation.x
215+
216+ changeTime(endLeadingConstraint, .end)
217+ view.setNeedsUpdateConstraints()
218+ }
219+
220+ recognizer.setTranslation(CGPoint.zero, in: self.intervalsStackView)
221+ }
222+
223+ func changeTime(_ constraint: NSLayoutConstraint, _ period: Period) {
224+ guard let startDate = startDate, let m = model else { return }
225+
226+ let offset = Int(constraint.constant) % Constants.widthTimePoint
227+
228+ if offset == 0 {
229+ let p = Int(constraint.constant) / Constants.widthTimePoint
230+ let time = startDate.addPeriods(p)
231+
232+ switch period {
233+ case .start:
234+ model = IntervalPickerModel(startTime: time,
235+ endTime: m.endTime)
236+ case .end:
237+ model = IntervalPickerModel(startTime: m.startTime,
238+ endTime: time)
239+ }
240+ }
241+ }
242+}
243+
244+extension IntervalPickerView {
245+ enum Constants {
246+ static let widthTimePoint = 11
247+
248+ static let adminDuration = 288
249+ static let clientDuration = 90
250+ // 12:00 = 12 * 6
251+ static let clientStartingPoint = 72
252+
253+ static let pointWidth: CGFloat = 19
254+ static let pointHeight: CGFloat = 85
255+
256+ static let minBookingLength: CGFloat = 132
257+
258+ static let heightIntervals = 85
259+ }
260+
261+ enum Period {
262+ case start
263+ case end
264+ }
265+}
Hookah Tau/Controllers/Picker view/IntervalPickerView.xibnew file+84
@@ -0,0 +1,84 @@
1+<?xml version="1.0" encoding="UTF-8"?>
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="retina4_7" orientation="portrait" appearance="light"/>
4+ <dependencies>
5+ <deployment identifier="iOS"/>
6+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
7+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
8+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9+ </dependencies>
10+ <objects>
11+ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
12+ <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
13+ <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="IntervalPickerView" customModule="Hookah_Tau" customModuleProvider="target">
14+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
15+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
16+ <subviews>
17+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QgM-NY-puV">
18+ <rect key="frame" x="0.0" y="21" width="414" height="85"/>
19+ <subviews>
20+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0TX-kl-OPO">
21+ <rect key="frame" x="0.0" y="84" width="414" height="1"/>
22+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
23+ <constraints>
24+ <constraint firstAttribute="height" constant="1" id="Sf4-dJ-qsD"/>
25+ </constraints>
26+ </view>
27+ <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wcb-MB-8F8">
28+ <rect key="frame" x="0.0" y="0.0" width="414" height="1"/>
29+ <color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
30+ <constraints>
31+ <constraint firstAttribute="height" constant="1" id="DCx-L0-09t"/>
32+ </constraints>
33+ </view>
34+ </subviews>
35+ <color key="backgroundColor" red="0.89019607840000003" green="0.89019607840000003" blue="0.89019607840000003" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
36+ <constraints>
37+ <constraint firstAttribute="height" constant="85" id="1jc-QZ-f8e"/>
38+ <constraint firstItem="Wcb-MB-8F8" firstAttribute="top" secondItem="QgM-NY-puV" secondAttribute="top" id="2tI-Or-OAH"/>
39+ <constraint firstItem="0TX-kl-OPO" firstAttribute="leading" secondItem="QgM-NY-puV" secondAttribute="leading" id="ZM5-CV-OL9"/>
40+ <constraint firstItem="Wcb-MB-8F8" firstAttribute="leading" secondItem="QgM-NY-puV" secondAttribute="leading" id="adB-mX-tf3"/>
41+ <constraint firstAttribute="bottom" secondItem="0TX-kl-OPO" secondAttribute="bottom" id="iji-xK-gU8"/>
42+ <constraint firstAttribute="trailing" secondItem="Wcb-MB-8F8" secondAttribute="trailing" id="kK5-X5-zT5"/>
43+ <constraint firstAttribute="trailing" secondItem="0TX-kl-OPO" secondAttribute="trailing" id="x2r-sO-Npw"/>
44+ </constraints>
45+ </view>
46+ <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e97-Yk-hHi">
47+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
48+ <subviews>
49+ <stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4n8-V3-m5t">
50+ <rect key="frame" x="0.0" y="0.0" width="414" height="106"/>
51+ </stackView>
52+ </subviews>
53+ <constraints>
54+ <constraint firstItem="4n8-V3-m5t" firstAttribute="height" secondItem="e97-Yk-hHi" secondAttribute="height" id="8p9-v6-iaH"/>
55+ <constraint firstAttribute="bottom" secondItem="4n8-V3-m5t" secondAttribute="bottom" id="ajS-3D-lcm"/>
56+ <constraint firstItem="4n8-V3-m5t" firstAttribute="top" secondItem="e97-Yk-hHi" secondAttribute="top" id="eN5-bZ-4Wt"/>
57+ <constraint firstItem="4n8-V3-m5t" firstAttribute="width" secondItem="e97-Yk-hHi" secondAttribute="width" id="enn-uv-GR2"/>
58+ <constraint firstAttribute="trailing" secondItem="4n8-V3-m5t" secondAttribute="trailing" id="fIk-7A-mrL"/>
59+ <constraint firstItem="4n8-V3-m5t" firstAttribute="leading" secondItem="e97-Yk-hHi" secondAttribute="leading" id="fcr-TD-pYK"/>
60+ </constraints>
61+ <viewLayoutGuide key="contentLayoutGuide" id="sxJ-gI-ilG"/>
62+ <viewLayoutGuide key="frameLayoutGuide" id="yj8-fL-J9D"/>
63+ </scrollView>
64+ </subviews>
65+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
66+ <constraints>
67+ <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="QgM-NY-puV" secondAttribute="bottom" id="JdC-xm-YdY"/>
68+ <constraint firstItem="e97-Yk-hHi" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="Oqv-Y6-Qp3"/>
69+ <constraint firstItem="e97-Yk-hHi" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="YH5-He-gHk"/>
70+ <constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="QgM-NY-puV" secondAttribute="trailing" id="ewc-Nf-fIZ"/>
71+ <constraint firstItem="QgM-NY-puV" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="jGH-FL-IC0"/>
72+ <constraint firstItem="e97-Yk-hHi" firstAttribute="bottom" secondItem="vUN-kp-3ea" secondAttribute="bottom" id="lOW-0Q-pvW"/>
73+ <constraint firstItem="e97-Yk-hHi" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" symbolic="YES" id="x39-q5-hdD"/>
74+ </constraints>
75+ <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
76+ <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
77+ <connections>
78+ <outlet property="intervalsStackView" destination="4n8-V3-m5t" id="239-zR-iS1"/>
79+ <outlet property="scrollView" destination="e97-Yk-hHi" id="eED-qd-4Yw"/>
80+ </connections>
81+ <point key="canvasLocation" x="137.68115942028987" y="360.26785714285711"/>
82+ </view>
83+ </objects>
84+</document>
Hookah Tau/DB/DataStorage.swift+15
@@ -18,6 +18,7 @@ class DataStorage {
1818 case isAdmin
1919 case loggedIn
2020 case cookies
21+ case chosenEstablishment // only for admin
2122 }
2223
2324 private init() {}
@@ -42,6 +43,15 @@ class DataStorage {
4243 UserDefaults.standard.setValue(isAdmin, forKey: Keys.isAdmin.rawValue)
4344 }
4445
46+ /// Сохраняем выбранный администратором establishmentId
47+ func setupEstablishmentChoice(id: Int) {
48+ UserDefaults.standard.setValue(id, forKey: Keys.chosenEstablishment.rawValue)
49+ }
50+
51+ func getEstablishmentChoice() -> Int? {
52+ let val = UserDefaults.standard.integer(forKey: Keys.chosenEstablishment.rawValue)
53+ return val == 0 ? nil : val
54+ }
4555
4656 /// Метод для установки куков в хранилище
4757 /// - Parameter cookies: куки
@@ -95,6 +105,11 @@ class DataStorage {
95105 }
96106
97107 func setLoggedInState(_ value: Bool) {
108+ if !value {
109+ UserDefaults.standard.removeObject(forKey: Keys.name.rawValue)
110+ UserDefaults.standard.removeObject(forKey: Keys.phone.rawValue)
111+ UserDefaults.standard.removeObject(forKey: Keys.cookies.rawValue)
112+ }
98113 UserDefaults.standard.set(value, forKey: Keys.loggedIn.rawValue)
99114 }
100115}
Hookah Tau/Extensions+String.swiftnew file+36
@@ -0,0 +1,36 @@
1+//
2+// Extensions+String.swift
3+// Hookah Tau
4+//
5+// Created by cstore on 10/12/2019.
6+// Copyright © 2019 Daria Rednikina. All rights reserved.
7+//
8+
9+import Foundation
10+
11+
12+extension String {
13+
14+ /// Only works with 9-length strings, for ex 01 733 01 79: (2 3 2 2)
15+ func formattedNumber() -> String {
16+ let cleanPhoneNumber = self.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
17+
18+ var result = ""
19+ let phoneMask = "+7 (9XX) XXX-XX-XX"
20+ var index = cleanPhoneNumber.startIndex
21+ for ch in phoneMask where index < cleanPhoneNumber.endIndex {
22+ if ch == "X" {
23+ result.append(cleanPhoneNumber[index])
24+ index = cleanPhoneNumber.index(after: index)
25+ } else {
26+ result.append(ch)
27+ }
28+ }
29+ return result
30+ }
31+
32+ /// Gets +7 9(XX) XXX-XX-XX returns XXXXXXXXX
33+ func toApiPhoneNumberFormat() -> String {
34+ return String(self.filter{ $0.isNumber }.dropFirst(2))
35+ }
36+}
Hookah Tau/Info.plist+5−3
@@ -2,8 +2,8 @@
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>
5+ <key>ITSAppUsesNonExemptEncryption</key>
6+ <false/>
77 <key>CFBundleDevelopmentRegion</key>
88 <string>$(DEVELOPMENT_LANGUAGE)</string>
99 <key>CFBundleExecutable</key>
@@ -19,7 +19,7 @@
1919 <key>CFBundleShortVersionString</key>
2020 <string>1.0</string>
2121 <key>CFBundleVersion</key>
22− <string>1</string>
22+ <string>$(CURRENT_PROJECT_VERSION)</string>
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+15
@@ -47,3 +47,18 @@ struct ReservationData: MyCodable {
4747 var reservedTable: Int
4848
4949}
50+
51+/// Структура для получения респонса на `GET /admin/reservations/{establishment id}`
52+struct ReservationWithUser: MyCodable {
53+
54+ var uuid: String
55+ var establishment: Int
56+ var startTime: Date
57+ var endTime: Date
58+ var numberOfGuests: Int
59+ var reservedTable: Int
60+ var owner: FullUser
61+
62+}
63+
64+
Hookah Tau/Models/User.swift+9
@@ -8,8 +8,17 @@
88
99import Foundation
1010
11+/// Используется в запросах при регистрации пользователя
1112struct User: MyCodable {
1213 var name: String
1314 var phoneNumber: String
1415 var isAdmin: Bool
1516}
17+
18+/// При запросах на список пользователей в аккаунте администратора
19+struct FullUser: MyCodable {
20+ var uuid: String
21+ var name: String?
22+ var phoneNumber: String
23+ var isAdmin: Bool
24+}
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/arrow.imageset/Contents.jsonnew file+15
@@ -0,0 +1,15 @@
1+{
2+ "images" : [
3+ {
4+ "idiom" : "universal",
5+ "filename" : "arrow.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/arrow.imageset/arrow.pdfnew file

Binary file not shown.

Hookah Tau/Views/Phone/PhoneView.swift+1−2
@@ -24,9 +24,8 @@ class PhoneView: UIView {
2424 phone?.attributedPlaceholder = NSAttributedString(string:"+ 7 (901) 733 01 79", attributes: [NSAttributedString.Key.foregroundColor: UIColor.lightGray])
2525 }
2626
27− // TODO: - change in future
2827 func bind(withModel model: String) {
29− phone?.text = model
28+ phone?.text = model.formattedNumber()
3029 }
3130}
3231