diff --git a/stillasMobileApplication/stillasMobileApplication.xcodeproj/project.pbxproj b/stillasMobileApplication/stillasMobileApplication.xcodeproj/project.pbxproj index efc207c09a8bd1d45555f7071db8f37ae247f169..75ddafcb518980e4d724651ad89521f47253da04 100644 --- a/stillasMobileApplication/stillasMobileApplication.xcodeproj/project.pbxproj +++ b/stillasMobileApplication/stillasMobileApplication.xcodeproj/project.pbxproj @@ -7,9 +7,53 @@ objects = { /* Begin PBXBuildFile section */ + 2723CF5C27FC6E4500210416 /* TransfereScaffolding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723CF5B27FC6E4500210416 /* TransfereScaffolding.swift */; }; + 2723CF8527FECE6E00210416 /* CacheEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723CF8427FECE6E00210416 /* CacheEntry.swift */; }; + 2723CF8827FEF72C00210416 /* CacheImplementation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723CF8727FEF72C00210416 /* CacheImplementation.swift */; }; + 2723CF8A27FEFA1400210416 /* ProjectData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2723CF8927FEFA1400210416 /* ProjectData.swift */; }; + 2736C04D27FAD2E90038BD7B /* ProjectInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2736C04C27FAD2E90038BD7B /* ProjectInfoView.swift */; }; + 27373E7B280EAF0100C83150 /* FilterProjectArea.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27373E7A280EAF0100C83150 /* FilterProjectArea.swift */; }; + 27373E7D280F10F100C83150 /* FilterProjectSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27373E7C280F10F100C83150 /* FilterProjectSize.swift */; }; + 27373E7F281023D900C83150 /* FilterProjectPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27373E7E281023D900C83150 /* FilterProjectPeriod.swift */; }; 274C541127EE02F2002CE76A /* BlurView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541027EE02F2002CE76A /* BlurView.swift */; }; - 274C541327EE0316002CE76A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541227EE0316002CE76A /* MapView.swift */; }; - 274C541527EE0339002CE76A /* CustomCorners.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541427EE0339002CE76A /* CustomCorners.swift */; }; + 274C541827F1B0A5002CE76A /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541727F1B0A5002CE76A /* MapView.swift */; }; + 274C541A27F1B277002CE76A /* NavigationBarBottom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541927F1B277002CE76A /* NavigationBarBottom.swift */; }; + 274C541D27F1B44C002CE76A /* ScaffoldingTransfere.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C541C27F1B44C002CE76A /* ScaffoldingTransfere.swift */; }; + 274C542427F1D0DC002CE76A /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C542327F1D0DC002CE76A /* ProfileView.swift */; }; + 274C542627F1D133002CE76A /* ProjectListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C542527F1D133002CE76A /* ProjectListView.swift */; }; + 274C542827F1DEC3002CE76A /* CircleImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C542727F1DEC3002CE76A /* CircleImage.swift */; }; + 274C542E27F1F3CD002CE76A /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C542D27F1F3CD002CE76A /* User.swift */; }; + 274C543227F311E6002CE76A /* MapDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C543127F311E6002CE76A /* MapDisplay.swift */; }; + 274C543427F34978002CE76A /* MapViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C543327F34977002CE76A /* MapViewModel.swift */; }; + 274C543927F4889C002CE76A /* Project.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274C543827F4889C002CE76A /* Project.swift */; }; + 27D2D6CA282AF0DD0041D014 /* ProfileData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D2D6C9282AF0DC0041D014 /* ProfileData.swift */; }; + 27D2D6CC282AF1040041D014 /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D2D6CB282AF1040041D014 /* Profile.swift */; }; + 27E980A5281710CC00118DB3 /* FilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980A4281710CC00118DB3 /* FilterView.swift */; }; + 27E980AB2819D4C100118DB3 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980AA2819D4C100118DB3 /* CalendarView.swift */; }; + 27E980AD2819D8DE00118DB3 /* CheckBoxRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980AC2819D8DE00118DB3 /* CheckBoxRow.swift */; }; + 27E980B0281B085100118DB3 /* ActiveSizeFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980AF281B085100118DB3 /* ActiveSizeFilterView.swift */; }; + 27E980B3281B089300118DB3 /* ActiveAreaFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980B2281B089300118DB3 /* ActiveAreaFilterView.swift */; }; + 27E980B5281B08C000118DB3 /* ActivePeriodFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980B4281B08C000118DB3 /* ActivePeriodFilterView.swift */; }; + 27E980B7281B0A1A00118DB3 /* FilterProjectStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980B6281B0A1A00118DB3 /* FilterProjectStatus.swift */; }; + 27E980BB281B26B900118DB3 /* ActiveStatusFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980BA281B26B900118DB3 /* ActiveStatusFilterView.swift */; }; + 27E980BD281B2A6C00118DB3 /* SizeBetweenFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980BC281B2A6C00118DB3 /* SizeBetweenFilter.swift */; }; + 27E980BF281B383200118DB3 /* SizeGreaterThanFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980BE281B383200118DB3 /* SizeGreaterThanFilter.swift */; }; + 27E980C1281B384600118DB3 /* SizeLessThanFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980C0281B384600118DB3 /* SizeLessThanFilter.swift */; }; + 27E980C4281BE49200118DB3 /* CornerRadiusStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980C3281BE49200118DB3 /* CornerRadiusStyle.swift */; }; + 27E980C8281BFA6D00118DB3 /* ScaffoldingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980C7281BFA6D00118DB3 /* ScaffoldingView.swift */; }; + 27E980CA281BFAA700118DB3 /* ProjectInfoDetailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980C9281BFAA700118DB3 /* ProjectInfoDetailedView.swift */; }; + 27E980CD281E9F5300118DB3 /* TransfereScaffoldingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980CC281E9F5300118DB3 /* TransfereScaffoldingButton.swift */; }; + 27E980CF281E9F9800118DB3 /* ScaffoldingItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980CE281E9F9800118DB3 /* ScaffoldingItems.swift */; }; + 27E980D1281E9FBB00118DB3 /* ScaffoldingItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980D0281E9FBB00118DB3 /* ScaffoldingItem.swift */; }; + 27E980D42822EC8100118DB3 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 27E980D32822EC8100118DB3 /* FirebaseAuth */; }; + 27E980D82822F50B00118DB3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 27E980D72822F50A00118DB3 /* GoogleService-Info.plist */; }; + 27E980DD282409CC00118DB3 /* SignInView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980DC282409CC00118DB3 /* SignInView.swift */; }; + 27E980DF28240A1E00118DB3 /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980DE28240A1E00118DB3 /* SignUpView.swift */; }; + 27E980E128240A5200118DB3 /* AppViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980E028240A5200118DB3 /* AppViewModel.swift */; }; + 27E980E42829AE5E00118DB3 /* TransfereScaffoldingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980E32829AE5E00118DB3 /* TransfereScaffoldingView.swift */; }; + 27E980E62829B0E600118DB3 /* HistoryOfScaffolding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980E52829B0E600118DB3 /* HistoryOfScaffolding.swift */; }; + 27E980E82829B3A700118DB3 /* ScaffoldingDetailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980E72829B3A700118DB3 /* ScaffoldingDetailedView.swift */; }; + 27E980EB2829B5F200118DB3 /* TextFieldModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27E980EA2829B5F200118DB3 /* TextFieldModifiers.swift */; }; 4D17100627D755400026C216 /* stillasMobileApplicationApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D17100527D755400026C216 /* stillasMobileApplicationApp.swift */; }; 4D17100827D755400026C216 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D17100727D755400026C216 /* ContentView.swift */; }; 4D17100A27D755410026C216 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4D17100927D755410026C216 /* Assets.xcassets */; }; @@ -17,9 +61,53 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 2723CF5B27FC6E4500210416 /* TransfereScaffolding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransfereScaffolding.swift; sourceTree = "<group>"; }; + 2723CF8427FECE6E00210416 /* CacheEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheEntry.swift; sourceTree = "<group>"; }; + 2723CF8727FEF72C00210416 /* CacheImplementation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheImplementation.swift; sourceTree = "<group>"; }; + 2723CF8927FEFA1400210416 /* ProjectData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectData.swift; sourceTree = "<group>"; }; + 2736C04C27FAD2E90038BD7B /* ProjectInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectInfoView.swift; sourceTree = "<group>"; }; + 27373E7A280EAF0100C83150 /* FilterProjectArea.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterProjectArea.swift; sourceTree = "<group>"; }; + 27373E7C280F10F100C83150 /* FilterProjectSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterProjectSize.swift; sourceTree = "<group>"; }; + 27373E7E281023D900C83150 /* FilterProjectPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterProjectPeriod.swift; sourceTree = "<group>"; }; 274C541027EE02F2002CE76A /* BlurView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurView.swift; sourceTree = "<group>"; }; - 274C541227EE0316002CE76A /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; }; - 274C541427EE0339002CE76A /* CustomCorners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCorners.swift; sourceTree = "<group>"; }; + 274C541727F1B0A5002CE76A /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; }; + 274C541927F1B277002CE76A /* NavigationBarBottom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarBottom.swift; sourceTree = "<group>"; }; + 274C541C27F1B44C002CE76A /* ScaffoldingTransfere.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaffoldingTransfere.swift; sourceTree = "<group>"; }; + 274C542327F1D0DC002CE76A /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; }; + 274C542527F1D133002CE76A /* ProjectListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectListView.swift; sourceTree = "<group>"; }; + 274C542727F1DEC3002CE76A /* CircleImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleImage.swift; sourceTree = "<group>"; }; + 274C542D27F1F3CD002CE76A /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; }; + 274C543127F311E6002CE76A /* MapDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDisplay.swift; sourceTree = "<group>"; }; + 274C543327F34977002CE76A /* MapViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModel.swift; sourceTree = "<group>"; }; + 274C543527F44104002CE76A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; + 274C543827F4889C002CE76A /* Project.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project.swift; sourceTree = "<group>"; }; + 27D2D6C9282AF0DC0041D014 /* ProfileData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileData.swift; sourceTree = "<group>"; }; + 27D2D6CB282AF1040041D014 /* Profile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profile.swift; sourceTree = "<group>"; }; + 27E980A4281710CC00118DB3 /* FilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterView.swift; sourceTree = "<group>"; }; + 27E980AA2819D4C100118DB3 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; }; + 27E980AC2819D8DE00118DB3 /* CheckBoxRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckBoxRow.swift; sourceTree = "<group>"; }; + 27E980AF281B085100118DB3 /* ActiveSizeFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSizeFilterView.swift; sourceTree = "<group>"; }; + 27E980B2281B089300118DB3 /* ActiveAreaFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveAreaFilterView.swift; sourceTree = "<group>"; }; + 27E980B4281B08C000118DB3 /* ActivePeriodFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePeriodFilterView.swift; sourceTree = "<group>"; }; + 27E980B6281B0A1A00118DB3 /* FilterProjectStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterProjectStatus.swift; sourceTree = "<group>"; }; + 27E980BA281B26B900118DB3 /* ActiveStatusFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveStatusFilterView.swift; sourceTree = "<group>"; }; + 27E980BC281B2A6C00118DB3 /* SizeBetweenFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeBetweenFilter.swift; sourceTree = "<group>"; }; + 27E980BE281B383200118DB3 /* SizeGreaterThanFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeGreaterThanFilter.swift; sourceTree = "<group>"; }; + 27E980C0281B384600118DB3 /* SizeLessThanFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeLessThanFilter.swift; sourceTree = "<group>"; }; + 27E980C3281BE49200118DB3 /* CornerRadiusStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerRadiusStyle.swift; sourceTree = "<group>"; }; + 27E980C7281BFA6D00118DB3 /* ScaffoldingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaffoldingView.swift; sourceTree = "<group>"; }; + 27E980C9281BFAA700118DB3 /* ProjectInfoDetailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectInfoDetailedView.swift; sourceTree = "<group>"; }; + 27E980CC281E9F5300118DB3 /* TransfereScaffoldingButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransfereScaffoldingButton.swift; sourceTree = "<group>"; }; + 27E980CE281E9F9800118DB3 /* ScaffoldingItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaffoldingItems.swift; sourceTree = "<group>"; }; + 27E980D0281E9FBB00118DB3 /* ScaffoldingItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaffoldingItem.swift; sourceTree = "<group>"; }; + 27E980D72822F50A00118DB3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; + 27E980DC282409CC00118DB3 /* SignInView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInView.swift; sourceTree = "<group>"; }; + 27E980DE28240A1E00118DB3 /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = "<group>"; }; + 27E980E028240A5200118DB3 /* AppViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewModel.swift; sourceTree = "<group>"; }; + 27E980E32829AE5E00118DB3 /* TransfereScaffoldingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransfereScaffoldingView.swift; sourceTree = "<group>"; }; + 27E980E52829B0E600118DB3 /* HistoryOfScaffolding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryOfScaffolding.swift; sourceTree = "<group>"; }; + 27E980E72829B3A700118DB3 /* ScaffoldingDetailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaffoldingDetailedView.swift; sourceTree = "<group>"; }; + 27E980EA2829B5F200118DB3 /* TextFieldModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldModifiers.swift; sourceTree = "<group>"; }; 4D17100227D755400026C216 /* stillasMobileApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = stillasMobileApplication.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4D17100527D755400026C216 /* stillasMobileApplicationApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = stillasMobileApplicationApp.swift; sourceTree = "<group>"; }; 4D17100727D755400026C216 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; @@ -32,12 +120,43 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 27E980D42822EC8100118DB3 /* FirebaseAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2723CF7E27FD9EA800210416 /* FetchingApiData */ = { + isa = PBXGroup; + children = ( + 2723CF8927FEFA1400210416 /* ProjectData.swift */, + 27D2D6C9282AF0DC0041D014 /* ProfileData.swift */, + ); + path = FetchingApiData; + sourceTree = "<group>"; + }; + 2723CF8627FEF60C00210416 /* Cache */ = { + isa = PBXGroup; + children = ( + 2723CF8427FECE6E00210416 /* CacheEntry.swift */, + 2723CF8727FEF72C00210416 /* CacheImplementation.swift */, + ); + path = Cache; + sourceTree = "<group>"; + }; + 27373E79280EAEBF00C83150 /* FilterData */ = { + isa = PBXGroup; + children = ( + 27E980AE281B083800118DB3 /* FilterView */, + 27E980A928193A8500118DB3 /* StatusFilter */, + 27E980A828193A7D00118DB3 /* PeriodFilter */, + 27E980A728193A7300118DB3 /* SizeFilter */, + 27E980A628193A6200118DB3 /* AreaFilter */, + ); + path = FilterData; + sourceTree = "<group>"; + }; 274C540E27EE02C4002CE76A /* BlurView */ = { isa = PBXGroup; children = ( @@ -46,24 +165,211 @@ path = BlurView; sourceTree = "<group>"; }; - 274C540F27EE02DD002CE76A /* MapView */ = { + 274C541627F1B097002CE76A /* MapView */ = { isa = PBXGroup; children = ( - 274C541227EE0316002CE76A /* MapView.swift */, + 274C541727F1B0A5002CE76A /* MapView.swift */, + 274C543127F311E6002CE76A /* MapDisplay.swift */, + 274C543327F34977002CE76A /* MapViewModel.swift */, ); path = MapView; sourceTree = "<group>"; }; + 274C541E27F1B4BB002CE76A /* Model */ = { + isa = PBXGroup; + children = ( + 2723CF7E27FD9EA800210416 /* FetchingApiData */, + 274C541C27F1B44C002CE76A /* ScaffoldingTransfere.swift */, + 274C542D27F1F3CD002CE76A /* User.swift */, + 274C543827F4889C002CE76A /* Project.swift */, + 27D2D6CB282AF1040041D014 /* Profile.swift */, + ); + path = Model; + sourceTree = "<group>"; + }; + 274C542927F1DEFC002CE76A /* Helpers */ = { + isa = PBXGroup; + children = ( + 274C542727F1DEC3002CE76A /* CircleImage.swift */, + 27E980AC2819D8DE00118DB3 /* CheckBoxRow.swift */, + 27E980C3281BE49200118DB3 /* CornerRadiusStyle.swift */, + 27E980EA2829B5F200118DB3 /* TextFieldModifiers.swift */, + ); + path = Helpers; + sourceTree = "<group>"; + }; + 274C542F27F2E03A002CE76A /* ProfileView */ = { + isa = PBXGroup; + children = ( + 274C542327F1D0DC002CE76A /* ProfileView.swift */, + ); + path = ProfileView; + sourceTree = "<group>"; + }; + 274C543027F2E049002CE76A /* ProjectView */ = { + isa = PBXGroup; + children = ( + 27373E79280EAEBF00C83150 /* FilterData */, + 27E980C5281BF9E100118DB3 /* ProjectView */, + ); + path = ProjectView; + sourceTree = "<group>"; + }; 27E5158527EE00D900EFE9A1 /* Views */ = { isa = PBXGroup; children = ( - 274C540F27EE02DD002CE76A /* MapView */, + 2723CF8627FEF60C00210416 /* Cache */, + 274C543027F2E049002CE76A /* ProjectView */, + 274C542F27F2E03A002CE76A /* ProfileView */, + 274C541E27F1B4BB002CE76A /* Model */, + 274C541627F1B097002CE76A /* MapView */, 274C540E27EE02C4002CE76A /* BlurView */, - 274C541427EE0339002CE76A /* CustomCorners.swift */, + 274C541927F1B277002CE76A /* NavigationBarBottom.swift */, + 274C542927F1DEFC002CE76A /* Helpers */, ); path = Views; sourceTree = "<group>"; }; + 27E980A628193A6200118DB3 /* AreaFilter */ = { + isa = PBXGroup; + children = ( + 27373E7A280EAF0100C83150 /* FilterProjectArea.swift */, + ); + path = AreaFilter; + sourceTree = "<group>"; + }; + 27E980A728193A7300118DB3 /* SizeFilter */ = { + isa = PBXGroup; + children = ( + 27E980C2281B386200118DB3 /* BetweenGreaterLess */, + 27373E7C280F10F100C83150 /* FilterProjectSize.swift */, + ); + path = SizeFilter; + sourceTree = "<group>"; + }; + 27E980A828193A7D00118DB3 /* PeriodFilter */ = { + isa = PBXGroup; + children = ( + 27373E7E281023D900C83150 /* FilterProjectPeriod.swift */, + 27E980AA2819D4C100118DB3 /* CalendarView.swift */, + ); + path = PeriodFilter; + sourceTree = "<group>"; + }; + 27E980A928193A8500118DB3 /* StatusFilter */ = { + isa = PBXGroup; + children = ( + 27E980B6281B0A1A00118DB3 /* FilterProjectStatus.swift */, + ); + path = StatusFilter; + sourceTree = "<group>"; + }; + 27E980AE281B083800118DB3 /* FilterView */ = { + isa = PBXGroup; + children = ( + 27E980B1281B087100118DB3 /* ActiveFilterViews */, + 27E980A4281710CC00118DB3 /* FilterView.swift */, + ); + path = FilterView; + sourceTree = "<group>"; + }; + 27E980B1281B087100118DB3 /* ActiveFilterViews */ = { + isa = PBXGroup; + children = ( + 27E980AF281B085100118DB3 /* ActiveSizeFilterView.swift */, + 27E980B2281B089300118DB3 /* ActiveAreaFilterView.swift */, + 27E980B4281B08C000118DB3 /* ActivePeriodFilterView.swift */, + 27E980BA281B26B900118DB3 /* ActiveStatusFilterView.swift */, + ); + path = ActiveFilterViews; + sourceTree = "<group>"; + }; + 27E980C2281B386200118DB3 /* BetweenGreaterLess */ = { + isa = PBXGroup; + children = ( + 27E980BC281B2A6C00118DB3 /* SizeBetweenFilter.swift */, + 27E980BE281B383200118DB3 /* SizeGreaterThanFilter.swift */, + 27E980C0281B384600118DB3 /* SizeLessThanFilter.swift */, + ); + path = BetweenGreaterLess; + sourceTree = "<group>"; + }; + 27E980C5281BF9E100118DB3 /* ProjectView */ = { + isa = PBXGroup; + children = ( + 274C542527F1D133002CE76A /* ProjectListView.swift */, + 27E980C6281BF9FE00118DB3 /* ProjectDetailView */, + ); + path = ProjectView; + sourceTree = "<group>"; + }; + 27E980C6281BF9FE00118DB3 /* ProjectDetailView */ = { + isa = PBXGroup; + children = ( + 27E980CB281E9F3900118DB3 /* ScaffoldingView */, + 2736C04C27FAD2E90038BD7B /* ProjectInfoView.swift */, + 27E980C9281BFAA700118DB3 /* ProjectInfoDetailedView.swift */, + ); + path = ProjectDetailView; + sourceTree = "<group>"; + }; + 27E980CB281E9F3900118DB3 /* ScaffoldingView */ = { + isa = PBXGroup; + children = ( + 27E980C7281BFA6D00118DB3 /* ScaffoldingView.swift */, + 27E980CE281E9F9800118DB3 /* ScaffoldingItems.swift */, + 27E980D0281E9FBB00118DB3 /* ScaffoldingItem.swift */, + 27E980E92829B49B00118DB3 /* ScaffoldingHistory */, + 27E980E22829A6E600118DB3 /* TransfereScaffolding */, + ); + name = ScaffoldingView; + sourceTree = "<group>"; + }; + 27E980D92824099100118DB3 /* Login&Signup */ = { + isa = PBXGroup; + children = ( + 27E980DB282409BB00118DB3 /* SignUp */, + 27E980DA282409B600118DB3 /* Login */, + 27E980E028240A5200118DB3 /* AppViewModel.swift */, + ); + path = "Login&Signup"; + sourceTree = "<group>"; + }; + 27E980DA282409B600118DB3 /* Login */ = { + isa = PBXGroup; + children = ( + 27E980DC282409CC00118DB3 /* SignInView.swift */, + ); + path = Login; + sourceTree = "<group>"; + }; + 27E980DB282409BB00118DB3 /* SignUp */ = { + isa = PBXGroup; + children = ( + 27E980DE28240A1E00118DB3 /* SignUpView.swift */, + ); + path = SignUp; + sourceTree = "<group>"; + }; + 27E980E22829A6E600118DB3 /* TransfereScaffolding */ = { + isa = PBXGroup; + children = ( + 2723CF5B27FC6E4500210416 /* TransfereScaffolding.swift */, + 27E980E32829AE5E00118DB3 /* TransfereScaffoldingView.swift */, + ); + name = TransfereScaffolding; + sourceTree = "<group>"; + }; + 27E980E92829B49B00118DB3 /* ScaffoldingHistory */ = { + isa = PBXGroup; + children = ( + 27E980E72829B3A700118DB3 /* ScaffoldingDetailedView.swift */, + 27E980CC281E9F5300118DB3 /* TransfereScaffoldingButton.swift */, + 27E980E52829B0E600118DB3 /* HistoryOfScaffolding.swift */, + ); + name = ScaffoldingHistory; + sourceTree = "<group>"; + }; 4D170FF927D755400026C216 = { isa = PBXGroup; children = ( @@ -83,7 +389,10 @@ 4D17100427D755400026C216 /* stillasMobileApplication */ = { isa = PBXGroup; children = ( + 27E980D72822F50A00118DB3 /* GoogleService-Info.plist */, + 274C543527F44104002CE76A /* Info.plist */, 27E5158527EE00D900EFE9A1 /* Views */, + 27E980D92824099100118DB3 /* Login&Signup */, 4D17100527D755400026C216 /* stillasMobileApplicationApp.swift */, 4D17100727D755400026C216 /* ContentView.swift */, 4D17100927D755410026C216 /* Assets.xcassets */, @@ -116,6 +425,9 @@ dependencies = ( ); name = stillasMobileApplication; + packageProductDependencies = ( + 27E980D32822EC8100118DB3 /* FirebaseAuth */, + ); productName = stillasMobileApplication; productReference = 4D17100227D755400026C216 /* stillasMobileApplication.app */; productType = "com.apple.product-type.application"; @@ -144,6 +456,9 @@ Base, ); mainGroup = 4D170FF927D755400026C216; + packageReferences = ( + 27E980D22822EC8100118DB3 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 4D17100327D755400026C216 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -159,6 +474,7 @@ buildActionMask = 2147483647; files = ( 4D17100D27D755410026C216 /* Preview Assets.xcassets in Resources */, + 27E980D82822F50B00118DB3 /* GoogleService-Info.plist in Resources */, 4D17100A27D755410026C216 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -170,11 +486,53 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 27E980A5281710CC00118DB3 /* FilterView.swift in Sources */, 4D17100827D755400026C216 /* ContentView.swift in Sources */, - 274C541327EE0316002CE76A /* MapView.swift in Sources */, + 27E980BD281B2A6C00118DB3 /* SizeBetweenFilter.swift in Sources */, + 274C541A27F1B277002CE76A /* NavigationBarBottom.swift in Sources */, + 27373E7B280EAF0100C83150 /* FilterProjectArea.swift in Sources */, + 27E980E82829B3A700118DB3 /* ScaffoldingDetailedView.swift in Sources */, + 2723CF5C27FC6E4500210416 /* TransfereScaffolding.swift in Sources */, + 27E980B7281B0A1A00118DB3 /* FilterProjectStatus.swift in Sources */, + 274C541827F1B0A5002CE76A /* MapView.swift in Sources */, + 27E980B5281B08C000118DB3 /* ActivePeriodFilterView.swift in Sources */, + 27E980E62829B0E600118DB3 /* HistoryOfScaffolding.swift in Sources */, + 27E980E128240A5200118DB3 /* AppViewModel.swift in Sources */, + 27E980DD282409CC00118DB3 /* SignInView.swift in Sources */, + 274C542827F1DEC3002CE76A /* CircleImage.swift in Sources */, + 27E980AB2819D4C100118DB3 /* CalendarView.swift in Sources */, + 27E980E42829AE5E00118DB3 /* TransfereScaffoldingView.swift in Sources */, + 27E980C1281B384600118DB3 /* SizeLessThanFilter.swift in Sources */, + 27373E7F281023D900C83150 /* FilterProjectPeriod.swift in Sources */, + 27E980B3281B089300118DB3 /* ActiveAreaFilterView.swift in Sources */, + 274C543227F311E6002CE76A /* MapDisplay.swift in Sources */, + 274C543427F34978002CE76A /* MapViewModel.swift in Sources */, + 2723CF8A27FEFA1400210416 /* ProjectData.swift in Sources */, + 274C542E27F1F3CD002CE76A /* User.swift in Sources */, + 27E980CA281BFAA700118DB3 /* ProjectInfoDetailedView.swift in Sources */, + 27E980B0281B085100118DB3 /* ActiveSizeFilterView.swift in Sources */, + 27E980D1281E9FBB00118DB3 /* ScaffoldingItem.swift in Sources */, + 27E980CF281E9F9800118DB3 /* ScaffoldingItems.swift in Sources */, + 27D2D6CC282AF1040041D014 /* Profile.swift in Sources */, + 27E980BF281B383200118DB3 /* SizeGreaterThanFilter.swift in Sources */, + 27E980DF28240A1E00118DB3 /* SignUpView.swift in Sources */, + 274C541D27F1B44C002CE76A /* ScaffoldingTransfere.swift in Sources */, + 27E980AD2819D8DE00118DB3 /* CheckBoxRow.swift in Sources */, + 27E980C4281BE49200118DB3 /* CornerRadiusStyle.swift in Sources */, + 274C542627F1D133002CE76A /* ProjectListView.swift in Sources */, + 2723CF8527FECE6E00210416 /* CacheEntry.swift in Sources */, + 2723CF8827FEF72C00210416 /* CacheImplementation.swift in Sources */, + 27E980EB2829B5F200118DB3 /* TextFieldModifiers.swift in Sources */, + 2736C04D27FAD2E90038BD7B /* ProjectInfoView.swift in Sources */, 4D17100627D755400026C216 /* stillasMobileApplicationApp.swift in Sources */, + 27373E7D280F10F100C83150 /* FilterProjectSize.swift in Sources */, 274C541127EE02F2002CE76A /* BlurView.swift in Sources */, - 274C541527EE0339002CE76A /* CustomCorners.swift in Sources */, + 27E980CD281E9F5300118DB3 /* TransfereScaffoldingButton.swift in Sources */, + 27E980C8281BFA6D00118DB3 /* ScaffoldingView.swift in Sources */, + 27D2D6CA282AF0DD0041D014 /* ProfileData.swift in Sources */, + 27E980BB281B26B900118DB3 /* ActiveStatusFilterView.swift in Sources */, + 274C543927F4889C002CE76A /* Project.swift in Sources */, + 274C542427F1D0DC002CE76A /* ProfileView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -232,7 +590,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 15.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -287,7 +645,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 15.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -305,20 +663,23 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"stillasMobileApplication/Preview Content\""; - DEVELOPMENT_TEAM = J3YJ2A9XWC; + DEVELOPMENT_TEAM = 9M752RTPAF; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = stillasMobileApplication/Info.plist; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "We use your location to show you where you are located on the map"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "com.-stillasMobileApplication"; + PRODUCT_BUNDLE_IDENTIFIER = bachelor.stillasMobileApplication; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -334,14 +695,17 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"stillasMobileApplication/Preview Content\""; - DEVELOPMENT_TEAM = J3YJ2A9XWC; + DEVELOPMENT_TEAM = 9M752RTPAF; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = stillasMobileApplication/Info.plist; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "We use your location to show you where you are located on the map"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.4; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -377,6 +741,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 27E980D22822EC8100118DB3 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 8.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 27E980D32822EC8100118DB3 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 27E980D22822EC8100118DB3 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 4D170FFA27D755400026C216 /* Project object */; } diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/UserProfile.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/UserProfile.imageset/Contents.json similarity index 100% rename from stillasMobileApplication/stillasMobileApplication/Assets.xcassets/UserProfile.imageset/Contents.json rename to stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/UserProfile.imageset/Contents.json diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/UserProfile.imageset/clipart2557794.png b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/UserProfile.imageset/clipart2557794.png similarity index 100% rename from stillasMobileApplication/stillasMobileApplication/Assets.xcassets/UserProfile.imageset/clipart2557794.png rename to stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Profile/UserProfile.imageset/clipart2557794.png diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Bunnskrue.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Bunnskrue.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1c2d8d3b921cd4cd937ee9c7d64cd5e14f5b81d5 Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Bunnskrue.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..339ea99989066ccd695d69377dc3b128503491ea --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Bunnskrue.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Bunnskrue.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..73c00596a7fca3f3d4bdd64053b69d86745f9e10 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..d34de197449b58c42290dd756599a45997aa6115 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Diagonalstag.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Diagonalstag.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Diagonalstag.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ec3a7f0e1827465a8ad6c846632277b32e90baf5 Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Diagonalstang.imageset/Diagonalstag.jpeg differ diff --git "a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Contents.json" "b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Contents.json" new file mode 100644 index 0000000000000000000000000000000000000000..d67644864c9b9d2f9244bd140e03f03618fc1fac --- /dev/null +++ "b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Enrørsbjelke.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Enr\303\270rsbjelke.jpeg" "b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Enr\303\270rsbjelke.jpeg" new file mode 100644 index 0000000000000000000000000000000000000000..d04f496791dbdca83754df6bb0e2277991ca1b80 Binary files /dev/null and "b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Enr\303\270rsbjelke.imageset/Enr\303\270rsbjelke.jpeg" differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..0317b454f4bd5d504d6604749af1ca1a0c4baf8c --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Gelender.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Gelender.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Gelender.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d41199e6ae56f2caee998430c4b79bc798ac823e Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Gelender.imageset/Gelender.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..1e0c9a00b1482bc05fb3308d9071bfcb0e027d9a --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Lengdebjelke.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Lengdebjelke.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Lengdebjelke.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..0678a5a2e0fa2885f57eb6e6c6e47916e643aff1 Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Lengdebjelke.imageset/Lengdebjelke.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..fe9fa12398b9939926ebfefa19823f96dc2bba56 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Plank.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Plank.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Plank.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..671dbcad3514964e973507422557c1615a8e02cd Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Plank.imageset/Plank.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..b2bdcbb6fc5b23134b9fdb1a69a9c0f230f55932 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Rekkverk.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Rekkverk.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Rekkverk.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e78503614d298f92503e0491fb80f1e99ea105f1 Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Rekkverksramme.imageset/Rekkverk.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..08ac02868fb8f5f55ec78e592bed3969feb63b80 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Spir.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Spir.png b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Spir.png new file mode 100644 index 0000000000000000000000000000000000000000..b66c8b479a6a83fe4d8c54cb7e021ee75a15820a Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Spir.imageset/Spir.png differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..ff8f716337ba742e3393e5e44cb9882933814384 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Stillaslem.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Stillaslem.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Stillaslem.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..82363e50cbbb937f771bcc06fd5a6d57d4797f8c Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Stillaslem.imageset/Stillaslem.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Contents.json b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..d072ea4527adb91e3b9bafe2153c29099301c93b --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Trapp.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Trapp.jpeg b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Trapp.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..3b32dd6df4b8fbcba84914a492451c4a7356873d Binary files /dev/null and b/stillasMobileApplication/stillasMobileApplication/Assets.xcassets/Scaffolding/Trapp.imageset/Trapp.jpeg differ diff --git a/stillasMobileApplication/stillasMobileApplication/ContentView.swift b/stillasMobileApplication/stillasMobileApplication/ContentView.swift index 182e43c9701d0d341f2279f0b5d0499f29128f58..b4071db45969dc1e29107d7a3231630f933e47e1 100644 --- a/stillasMobileApplication/stillasMobileApplication/ContentView.swift +++ b/stillasMobileApplication/stillasMobileApplication/ContentView.swift @@ -7,13 +7,31 @@ import SwiftUI -/** - ContentView is responsible for the views in the application. - This will need enum and TabView on a later stage to switch between views. - */ +/// **ContentView** +/// Responsible for the views in the application. +/// This will need enum and TabView on a later stage to switch between views. +/// https://www.youtube.com/watch?v=vPCEIPL0U_k +/// https://firebase.google.com/docs/auth/ios/start struct ContentView: View { + @State var email = "" + @State var password = "" + + /// The model responsible for sign in and sign up + @EnvironmentObject var viewModel: AppViewModel + var body: some View { - NavigationBarBottom() + ZStack { + /// If user is signed in, give the user access to the application, if not prompt the user with the login and sign up view + if viewModel.signedIn { + NavigationBarBottom() + } else { + SignInView() + } + } + .onAppear { + /// Remembers if the user was signed in and closes the application + viewModel.signedIn = viewModel.isSignedIn + } } } diff --git a/stillasMobileApplication/stillasMobileApplication/GoogleService-Info.plist b/stillasMobileApplication/stillasMobileApplication/GoogleService-Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..5f32f9c1354c025d4769a34a03949508227bb06e --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/GoogleService-Info.plist @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CLIENT_ID</key> + <string>586975019426-gv1n44k324m6vvrujrskqtak9jt4k19n.apps.googleusercontent.com</string> + <key>REVERSED_CLIENT_ID</key> + <string>com.googleusercontent.apps.586975019426-gv1n44k324m6vvrujrskqtak9jt4k19n</string> + <key>API_KEY</key> + <string>AIzaSyAzuuzPV27C8W4xAOuMLZbWsBy9m7juKgc</string> + <key>GCM_SENDER_ID</key> + <string>586975019426</string> + <key>PLIST_VERSION</key> + <string>1</string> + <key>BUNDLE_ID</key> + <string>bachelor.stillasMobileApplication</string> + <key>PROJECT_ID</key> + <string>stillas-16563</string> + <key>STORAGE_BUCKET</key> + <string>stillas-16563.appspot.com</string> + <key>IS_ADS_ENABLED</key> + <false></false> + <key>IS_ANALYTICS_ENABLED</key> + <false></false> + <key>IS_APPINVITE_ENABLED</key> + <true></true> + <key>IS_GCM_ENABLED</key> + <true></true> + <key>IS_SIGNIN_ENABLED</key> + <true></true> + <key>GOOGLE_APP_ID</key> + <string>1:586975019426:ios:b582fd2bfebd7aa3bc32fb</string> +</dict> +</plist> \ No newline at end of file diff --git a/stillasMobileApplication/stillasMobileApplication/Login&Signup/AppViewModel.swift b/stillasMobileApplication/stillasMobileApplication/Login&Signup/AppViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..635dd004875d9a8ca22907f334318969f097cd80 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Login&Signup/AppViewModel.swift @@ -0,0 +1,77 @@ +// +// AppViewModel.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 05/05/2022. +// + +import FirebaseAuth + +/// **AppViewModel** +/// The prosessing class responsible for login and sign up through Firebase Authentication +/// Code taken from and inspired from the following sources: +/// https://www.youtube.com/watch?v=vPCEIPL0U_k +/// https://firebase.google.com/docs/auth/ios/start +class AppViewModel: ObservableObject { + + let auth = Auth.auth() + + /// Is user signed in? + @Published var signedIn: Bool = false + + /// Getter and Setter for the userID + var userID: String { + get { return auth.currentUser?.uid ?? "" } + set { self.userID = newValue } + } + + /// Getter for is user signed in? + var isSignedIn: Bool { + return auth.currentUser != nil + } + + + /// Attempts to sign in user to the application by authorizing the user through the Firebase Authentication + /// - Parameters: + /// - email: The email of the user + /// - password: The password of the user + func signIn(email: String, password: String) { + + /// Attempts to authorize the credentials in Firebase Authentication + auth.signIn(withEmail: email, password: password) { [weak self ] (result, error) in + guard result != nil, error == nil else { + return + } + DispatchQueue.main.async { + // Success + self?.signedIn = true + } + } + } + + + /// Attempts to register a new user to the application by adding them to the Firebase Authentication + /// - Parameters: + /// - email: The email of the new user + /// - password: The password of the new user + func signUp(email: String, password: String) { + /// Attempts to create a new user + auth.createUser(withEmail: email, password: password) { [weak self ] (result, error) in + guard result != nil, error == nil else { + return + } + DispatchQueue.main.async { + // Success + self?.signedIn = true + } + } + } + + + /// Signs the user out of the application + func signOut() { + try? auth.signOut() + + self.signedIn = false + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Login&Signup/Login/SignInView.swift b/stillasMobileApplication/stillasMobileApplication/Login&Signup/Login/SignInView.swift new file mode 100644 index 0000000000000000000000000000000000000000..a6a9e1e314b85373cf1b8579263c2c12fbc05b57 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Login&Signup/Login/SignInView.swift @@ -0,0 +1,102 @@ +// +// SignInView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 05/05/2022. +// + +import SwiftUI +import MapKit + +/// **SignInView** +/// The view responsible for the sign in for users +/// Code are inspired from: +/// https://medium.com/@success.anil.kk/login-screen-demo-with-swiftui-2f711e0c657d +/// https://www.youtube.com/watch?v=vPCEIPL0U_k +/// https://firebase.google.com/docs/auth/ios/start +struct SignInView: View { + @State var email = "" + @State var password = "" + + /// The model responsible for sign in + @EnvironmentObject var viewModel: AppViewModel + + let verticalPaddingForForm = 40.0 + + var body: some View { + NavigationView{ + ZStack { + /// Darkened background of the login View with the map in the shadows + ZStack { + /// Blur effect + Color.black.frame(width: UIScreen.screenWidth, height: UIScreen.screenHeight, alignment: .center).zIndex(1).opacity(0.5).ignoresSafeArea() + MapView() + .blur(radius: 10) + .allowsHitTesting(false) + } + + VStack(spacing: CGFloat(verticalPaddingForForm)) { + Text("Welcome To MBStillas ST") + .font(.title) + .bold() + HStack { + Image(systemName: "person") + .foregroundColor(.secondary) + + /// The login email + TextField("Enter your email", text: $email) + .foregroundColor(Color.black) + .autocapitalization(.none) + .disableAutocorrection(true) + } + .padding() + .background(Color.white) + .cornerRadius(10) + + HStack { + Image(systemName: "lock") + .foregroundColor(.secondary) + + /// The login password masked + SecureField("Enter password", text: $password) + .foregroundColor(Color.black) + .autocapitalization(.none) + .disableAutocorrection(true) + } + .padding() + .background(Color.white) + .cornerRadius(10) + + /// Sends the login request given that both email and password are filled out + Button(action: { + guard !email.isEmpty, !password.isEmpty else { + return + } + viewModel.signIn(email: email, password: password) + }) { + Text("Sign in") + .frame(width: 150, height: 50, alignment: .center) + } + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(10) + + /// Button to redirect to Sign up + NavigationLink("Sign up", destination: SignUpView()) + .foregroundColor(.white) + .padding() + .background(Color.black.opacity(0.2)) + .cornerRadius(10) + + } + .padding(.horizontal, CGFloat(verticalPaddingForForm)) + } + } + } +} + +struct SignInView_Previews: PreviewProvider { + static var previews: some View { + SignInView() + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Login&Signup/SignUp/SignUpView.swift b/stillasMobileApplication/stillasMobileApplication/Login&Signup/SignUp/SignUpView.swift new file mode 100644 index 0000000000000000000000000000000000000000..67b02d12822cef0b5b0533d10fd54fd6dd280407 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Login&Signup/SignUp/SignUpView.swift @@ -0,0 +1,53 @@ +// +// SignUpView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 05/05/2022. +// + +import SwiftUI +import FirebaseAuth + +/// **SignUpView** +/// The view responsible for the sign up page +/// Code taken and inspired from this youtube video and firebases authentication startup +/// https://www.youtube.com/watch?v=vPCEIPL0U_k +/// https://firebase.google.com/docs/auth/ios/start +// TODO: Add body where user inputs data in addition to username and password +struct SignUpView: View { + @State var email = "" + @State var password = "" + + /// The model responsible for sign up + @EnvironmentObject var viewModel: AppViewModel + + var body: some View { + VStack { + TextField("Email", text: $email) + .autocapitalization(.none) + .disableAutocorrection(true) + + /// SecureField to mask the password + SecureField("Password", text: $password) + .autocapitalization(.none) + .disableAutocorrection(true) + + /// Sends the request to sign up to the system + Button(action: { + guard !email.isEmpty, !password.isEmpty else { + return + } + viewModel.signUp(email: email, password: password) + }) { + Text("Sign up") + } + } + .padding() + } +} + +struct SignUpView_Previews: PreviewProvider { + static var previews: some View { + SignUpView() + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Resources/projectData.json b/stillasMobileApplication/stillasMobileApplication/Resources/projectData.json deleted file mode 100644 index e308079b474cae71847da84cbfa9731e780a6d4d..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Resources/projectData.json +++ /dev/null @@ -1,60 +0,0 @@ -[ - { - "projectID": 420, - "projectName": "Ntnu i gjøvik", - "latitude": 60.7905060889568, - "longitude": 10.681777071532371, - "period": { - "startDate": "20.02.2020", - "endDate": "10.05.2020" - }, - "size": 240, - "State": "Active", - "adresse":{ - "gate": "Piazza del Colosseo 1", - "postnummer": "0184", - "kommune": "Roma", - "fylke": "Roma" - }, - "Leier": { - "name": "Rome", - "number": 639967700 - }, - "Scaffolding": { - "units":[ - { - "type": "Spire", - "quantity": { - "expected": 3241, - "registered":3241 - } - }, - { - "type": "Flooring", - "quantity": { - "expected": 500000, - "registered":499211 - } - } - ] - }, - "geofence": { - "w-position": { - "latitude": 60.79077759591496, - "longitude": 10.683249543160402 - }, - "x-position": { - "latitude": 60.79015256651516, - "longitude": 10.684424851812308 - }, - "y-position":{ - "latitude": 60.789159847696716, - "longitude": 10.68094413003551 - }, - "z-position":{ - "latitude": 60.78963782726421, - "longitude": 10.680160590934236 - } - } - } -] diff --git a/stillasMobileApplication/stillasMobileApplication/Resources/userData.json b/stillasMobileApplication/stillasMobileApplication/Resources/userData.json deleted file mode 100644 index 90efc398979b96b4f93f615e517480326ca30e5b..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Resources/userData.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "id": 232, - "name": "Ola Nordmann", - "dateOfBirth": "01.04.1988", - "role": "Storage", - "admin": false, - "imageName": "UserProfile" - } -] diff --git a/stillasMobileApplication/stillasMobileApplication/Views/BlurView/BlurView.swift b/stillasMobileApplication/stillasMobileApplication/Views/BlurView/BlurView.swift index 5f5c1d116a7b1e71cd928bfceccef98035f335e9..2863563b9d9645339cdd7d7c3609ef21275edc3b 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/BlurView/BlurView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/BlurView/BlurView.swift @@ -7,7 +7,8 @@ import SwiftUI -// Blurred View +/// **BlurView** +/// A blur view struct BlurView: UIViewRepresentable { let style: UIBlurEffect.Style diff --git a/stillasMobileApplication/stillasMobileApplication/Views/FetchingApiData/ViewController.swift b/stillasMobileApplication/stillasMobileApplication/Views/FetchingApiData/ViewController.swift deleted file mode 100644 index f8df0e66d670c2b8557cc19935e428cbd0ce57d8..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/FetchingApiData/ViewController.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// ViewController.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 06/04/2022. -// -/* -import Foundation -import UIKit - -class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - } -} - -let url = "http://10.212.138.205:8080/stillastracking/v1/api/unit/" -var project: Project -*/ - - diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CheckBoxRow.swift b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CheckBoxRow.swift index e13ea2b834cfbbe29df9c8796a997b29957266a4..60ead4b2b96d2e5e882df7b768b1d583f4f81887 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CheckBoxRow.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CheckBoxRow.swift @@ -7,6 +7,8 @@ import SwiftUI +/// **CheckBoxRow** +/// Creates a checkbox row to be used in lists etc. struct CheckBoxRow: View { var title: String @Binding var selectedItems: Set<String> @@ -29,6 +31,8 @@ struct CheckBoxRow: View { } } +/// **CheckBoxView** +/// Creates the view of the checkboxes struct CheckBoxView: View { @Binding var checked: Bool @State var title: String diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CircleImage.swift b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CircleImage.swift index 788238b206ad32fe97159a1fae544bf5c617dd8c..0a65c93d29c7f2ce192cc0b61595556444f05738 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CircleImage.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CircleImage.swift @@ -7,6 +7,8 @@ import SwiftUI +/// **CircleImage** +/// Creates a circle frame with an image struct CircleImage: View { var image: Image diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CornerRadiusStyle.swift b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CornerRadiusStyle.swift new file mode 100644 index 0000000000000000000000000000000000000000..6d9d3d6408e34195fc4e81d4dbdde3f141d13a8f --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CornerRadiusStyle.swift @@ -0,0 +1,43 @@ +// +// CornerRadius.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 29/04/2022. +// + +import SwiftUI + +/// **CornedRadiusStyle** +/// Custom corner radius for corner +struct CornerRadiusStyle: ViewModifier { + var radius: CGFloat + var corners: UIRectCorner + + struct CornerRadiusShape: Shape { + + var radius = CGFloat.infinity + var corners = UIRectCorner.allCorners + + func path(in rect: CGRect) -> Path { + let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) + return Path(path.cgPath) + } + } + + func body(content: Content) -> some View { + content + .clipShape(CornerRadiusShape(radius: radius, corners: corners)) + } +} + +extension View { + func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { + ModifiedContent(content: self, modifier: CornerRadiusStyle(radius: radius, corners: corners)) + } +} +/* +struct CornerRadius_Previews: PreviewProvider { + static var previews: some View { + CornerRadius() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CustomCorners.swift b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CustomCorners.swift deleted file mode 100644 index ed7e837a6d1ecd7ff3333665c24235138a6b79bc..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/CustomCorners.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// CustomCorners.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 24/03/2022. -// - -import SwiftUI - -struct CustomCorners: Shape { - var corners: UIRectCorner - var radius: CGFloat - - func path(in rect: CGRect) -> Path { - let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) - - return Path(path.cgPath) - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Helpers/TextFieldModifiers.swift b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/TextFieldModifiers.swift new file mode 100644 index 0000000000000000000000000000000000000000..6224cd3409b45c58e142605861ee89d4b81a891a --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Helpers/TextFieldModifiers.swift @@ -0,0 +1,117 @@ +// +// TextFieldModifiers.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 09/05/2022. +// + +import SwiftUI + +/// **SearchBar** +/// UIViewRepresentable which adds a searchbar to a list in transfere scaffolding +/// This is taken from: +/// https://roddy.io/2020/09/07/add-search-bar-to-swiftui-picker/ +struct SearchBar: UIViewRepresentable { + + @Binding var text: String + var placeholder: String + + /// Makes a searchbar + func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar { + let searchBar = UISearchBar(frame: .zero) + searchBar.delegate = context.coordinator + + searchBar.placeholder = placeholder + searchBar.autocapitalizationType = .none + searchBar.searchBarStyle = .minimal + return searchBar + } + + /// Updates the searchbar + func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) { + uiView.text = text + } + + func makeCoordinator() -> SearchBar.Coordinator { + return Coordinator(text: $text) + } + + class Coordinator: NSObject, UISearchBarDelegate { + + @Binding var text: String + + init(text: Binding<String>) { + _text = text + } + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + text = searchText + } + } +} + +// TODO: Make this happen/work - optionally remove +struct NoProjectSelected: TextFieldStyle { + @Binding var focused: Bool + func _body(configuration: TextField<Self._Label>) -> some View { + configuration + .padding(10) + .background( + RoundedRectangle(cornerRadius: 10, style: .continuous) + .stroke(focused ? Color.red : Color.gray, lineWidth: 1) + ).padding() + } +} + +/// **TextFieldEmpty** +/// Checks if the textfield is empty, if so - make the border red +/// Code taken from and inspired from: +/// https://stackoverflow.com/questions/60379010/how-to-change-swiftui-textfield-style-after-tapping-on-it +struct TextFieldEmpty: TextFieldStyle { + @Binding var empty: Bool + func _body(configuration: TextField<Self._Label>) -> some View { + configuration + .padding(10) + .background( + RoundedRectangle(cornerRadius: 10, style: .continuous) + .stroke(empty ? Color.red : Color.gray, lineWidth: 1) + ).padding() + } +} + +/// **ClearButton** +/// The clear button for the textfield +/// Code taken from and inspired from: https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwjzjJC2tMP3AhUnSfEDHSwjC-0QFnoECAUQAQ&url=https%3A%2F%2Fsanzaru84.medium.com%2Fswiftui-how-to-add-a-clear-button-to-a-textfield-9323c48ba61c&usg=AOvVaw1aPoAd3QYr5ByERti3mGWj +struct ClearButton: ViewModifier { + /// The searchfield text + @Binding var text: String + + public func body(content: Content) -> some View { + ZStack(alignment: .trailing) { + content + if !text.isEmpty { + Button(action: { + self.text = "" + }) { + Image(systemName: "delete.left") + .foregroundColor(Color(UIColor.opaqueSeparator)) + } + .padding(.trailing, 20) + } + } + } +} + +/// **NumbersOnly** +/// Validates textfield input to be Int +class NumbersOnly: ObservableObject { + @Published var value = "" { + didSet { + let filtered = value.filter { $0.isNumber } + + if value != filtered { + value = filtered + } + } + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapDisplay.swift b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapDisplay.swift index 363c72774c941255801b18584f01dc620c45fe37..ff938d185bc29e2049dae4396fcfb865147ebda2 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapDisplay.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapDisplay.swift @@ -10,20 +10,22 @@ import MapKit import CoreLocation -/** - MapDisplay - makes a MKMapView and defines its properties like userTrackingMode and setRegion etc. - - This way to define a map was inspired by this resource, as it creates a MKMapView in a neat way. It also makes the process of displaying CheckPoints and GeoFences easier alongside with other map functionality. - https://iosapptemplates.com/blog/swiftui/map-view-swiftui - */ +/// **MapDisplay** +/// Makes a MKMapView and defines its properties like userTrackingMode and setRegion etc. +/// This way to define a map was inspired by this resource, as it creates a MKMapView in a neat way. It also makes the process of displaying CheckPoints and GeoFences easier alongside with other map functionality. +/// https://iosapptemplates.com/blog/swiftui/map-view-swiftui struct MapDisplay: UIViewRepresentable { /// A property wrapper type that instantiates an observable object of type MapViewModel() @StateObject private var viewModel = MapViewModel() - - /** - makeUIView() - Makes the MKMapView - Allows to show user location, sets tracking mode and region of interest on "open" - */ + @State var projects: [Project] = [Project]() + + + + + /// Makes the MKMapView + /// Allows to show user location, sets tracking mode and region of interest on "open" + /// - Parameter context: A context structure containing information about the current state of the system + /// - Returns: A MKMapView func makeUIView(context: Context) -> MKMapView { let mapView = MKMapView(frame: UIScreen.main.bounds) mapView.showsUserLocation = true @@ -33,23 +35,24 @@ struct MapDisplay: UIViewRepresentable { return mapView } + /// Updates the MKMapView + /// - Parameter uiView: The MKMapView to be updated func updateUIView(_ uiView: MKMapView) { - let projVi = ProjectView() - - let annotations = projVi.projectsArr.map { project -> MKAnnotation in + //ProjectData().loadData(completion: @escaping projects) + let annotations = ProjectListView().projects.map { project -> MKAnnotation in let annotation = MKPointAnnotation() annotation.title = project.projectName annotation.subtitle = "\(project.projectID)" annotation.coordinate = CLLocationCoordinate2D(latitude: project.latitude, longitude: project.longitude) + print(annotation) return annotation } uiView.addAnnotations(annotations) } - /** - updateUIView() - Updates the state of the MKMapView with the changed information from SwiftUI - */ + /// **updateUIView** + /// Updates the state of the MKMapView with the changed information from SwiftUI func updateUIView(_ uiView: MKMapView, context: Context) { + } } - diff --git a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapView.swift b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapView.swift index 93c229be08433816a3790550ad8e8f8b5d306c92..c7f5c0f7f3877d5ce42e71bb7cc832fc7aac7e88 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapView.swift @@ -9,15 +9,15 @@ import SwiftUI import UIKit import MapKit -/** - A MapView responsible for displaying the Apple Maps in the application. - */ +/// **MapView** +/// Is responsible for displaying the Apple Maps in the application. struct MapView: View { /// A property wrapper type that instantiates an observable object of type MapViewModel() @StateObject private var viewModel = MapViewModel() @State private var searchText = "" @State private var dismissedAlready = false - + //@State var projects = [Project]() + var body: some View { VStack { GeometryReader { proxy in @@ -27,6 +27,7 @@ struct MapView: View { .onAppear { viewModel.checkIfLocationServicesIsEnabled() } + /// Displays an alert to the user if the location services are disabled, recommending the user to enable them and suggesting a redirect to the location service settings for the application. /// This alert is inspired from: https://www.hackingwithswift.com/forums/swiftui/getting-error-when-trying-to-change-location-authorisation/9216 .alert(isPresented: $viewModel.locationPermissionDenied, @@ -40,20 +41,25 @@ struct MapView: View { secondaryButton: .cancel(Text("Dismiss"), action: { setLocationPermissionFalse() })) }) + /*.task { + await ProjectData().loadData { (projects) in + self.projects = projects + } + }*/ } } + } - /** - Dismisses the alert - */ + + /// Dismisses the alert func setLocationPermissionFalse() { viewModel.locationPermissionDenied = false } +} - struct MapView_Previews: PreviewProvider { - static var previews: some View { - MapView() - } +struct MapView_Previews: PreviewProvider { + static var previews: some View { + MapView() } } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapViewModel.swift b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapViewModel.swift index 78e89915f3e1df90d13dad040e04980ef5947e8d..8fcbbc2b966f4343138777dac3236046aaa71911 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapViewModel.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/MapView/MapViewModel.swift @@ -9,10 +9,9 @@ import Foundation import MapKit import SwiftUI -/** - MapDetails - enum values that are used multiple places. - Used to abstract reused code - */ +/// **MapDetails** +/// Enum values that are used multiple places. +/// Used to abstract reused code. enum MapDetails { /// Sets the starting location of the map to be Gjøvik (latitude: 60.79574, longitude: 10.69155) static let startingLocation = CLLocationCoordinate2D( @@ -20,18 +19,17 @@ enum MapDetails { longitude: 10.69155 ) /// Sets zoom level of map on initialization - /// Closer to zero is more zoomed in + /// Closer to zero is more zoomed in static let defaultSpan = MKCoordinateSpan( latitudeDelta: 0.03, longitudeDelta: 0.03 ) } -/** - Class responsible for checking if the user has enabled location services. - This class is inspired a lot by the Apple Development documentation as well as this youtube video: - https://www.youtube.com/watch?v=hWMkimzIQoU - */ +/// **MapViewModel** +/// Class responsible for checking if the user has enabled location services. +/// This class is inspired a lot by the Apple Development documentation as well as this youtube video: +/// https://www.youtube.com/watch?v=hWMkimzIQoU final class MapViewModel: NSObject, ObservableObject, CLLocationManagerDelegate { @Published var locationPermissionDenied = false @Published var dismissCount = 0 @@ -45,9 +43,7 @@ final class MapViewModel: NSObject, ObservableObject, CLLocationManagerDelegate var locationManager: CLLocationManager? - /** - checkIfLocationServicesIsEnabled() - Checks if the user has enabled location services. - */ + /// checkIfLocationServicesIsEnabled() - Checks if the user has enabled location services. func checkIfLocationServicesIsEnabled() { if CLLocationManager.locationServicesEnabled() { locationManager = CLLocationManager() @@ -61,9 +57,8 @@ final class MapViewModel: NSObject, ObservableObject, CLLocationManagerDelegate } // TODO: Check -> fatal error when location services are off? - /** - checkLocationAuthorization() - Checks which authorization the application is assigned to. - */ + /// checkLocationAuthorization + /// Checks which authorization the application is assigned to. private func checkLocationAutorization() { guard let locationManager = locationManager else { return } @@ -90,9 +85,7 @@ final class MapViewModel: NSObject, ObservableObject, CLLocationManagerDelegate } } - /** - locationManagerDidChangeAuthorization() - Checks the autorization on locationManager creation as well as if the apps authorization changes - */ + /// locationManagerDidChangeAuthorization() - Checks the autorization on locationManager creation as well as if the apps authorization changes func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { checkLocationAutorization() } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/MapView_Old/MapView_Old.swift b/stillasMobileApplication/stillasMobileApplication/Views/MapView_Old/MapView_Old.swift deleted file mode 100644 index db8e1365409ebf18ce061b895da2def2a7de33b5..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/MapView_Old/MapView_Old.swift +++ /dev/null @@ -1,246 +0,0 @@ -// -// MapView.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 24/03/2022. -// -/* -import SwiftUI -import UIKit -import MapKit - - -struct ScaffoldingUnit: Identifiable { - let id = UUID() - let name: String - let size: String - let amount: Int -} - -struct ScaffoldingUnitRow: View { - var scaffolding: ScaffoldingUnit - - var body: some View { - HStack { - VStack(alignment: .leading) { - Text(scaffolding.name) - Text(scaffolding.size).font(.subheadline).foregroundColor(.gray) - } - Spacer() - Text(String(format: "%d", scaffolding.amount)) - .foregroundColor(.gray) - } - } -} - -/** - A MapView responsible for displaying the Apple Maps in the application. - - Inspiration taken from this youtube video: - https://www.youtube.com/watch?v=CyMtjSspJZA - */ -struct MapView_Old: View { - @State private var isInitialOffsetSet = false - @State private var searchText = "" - - var body: some View { - ZStack { - GeometryReader { proxy in - /// MapViewMap responsible for the map view - MapViewMap() - - /// DrawerView responsible for the drawer slide - DrawerView() - } - } -} - - struct DrawerView: View { - //@State var searchQuery = "" - @State var offset: CGFloat = 0 - @State var lastOffset: CGFloat = 0 - @GestureState var gestureOffset: CGFloat = 0 - @State private var isInitialOffsetSet = false - let height = 0 - - - /*let scaffoldingUnits = [ - ScaffoldingUnit(name: "Spir", size: "2m", amount: 1400), - ScaffoldingUnit(name: "Spir", size: "3m", amount: 1500), - ScaffoldingUnit(name: "Lengdebjelke", size: "3m", amount: 480) - /*"Spir 3m", "Spir 2m", "Bærebjelke", "Trapp", "UTV Trapp", "Bunnskrue", "Diagonalstag DS", "Stillaslem Alu", "AL plank B-230 mm", "Rekkverk", "Enrørsbjelke", "Horisontaler"*/ - ]*/ - - - var body: some View { - GeometryReader { proxy in - let height = proxy.frame(in: .global).height - ZStack { - BlurView(style: .systemMaterial) - .clipShape(CustomCorners(corners: [.topLeft, .topRight], radius: 15)) - - VStack { - Capsule() - .fill(Color.gray) - .frame(width: 40, height: 5) - .padding(.top, 7) - - NavView() - /*ZStack { - NavigationView { - List (searchResults) { scaffolding in - NavigationLink(destination: DetailView(scaffolding: scaffolding)) { ScaffoldingUnitRow (scaffolding: scaffolding) } - } - //.listRowBackground(Color.red) - .listStyle(PlainListStyle()) - .searchable(text: $searchQuery) - .navigationTitle("Scaffolding units") - } - .navigationViewStyle(StackNavigationViewStyle()) - //.ignoresSafeArea(.all, edges: .horizontal) - //.frame(minWidth: 0, idealWidth: proxy.frame(in: .global).width, maxWidth: .infinity, minHeight: 0, idealHeight: proxy.frame(in: .global).height, maxHeight: .infinity, alignment: .topLeading) - - }*/ - } - .padding(.horizontal) - .frame(maxHeight: .infinity, alignment: .top) - } - .offset(y: height - 100) - .offset(y: -offset > 0 ? -offset <= (height - 100) ? offset : -(height - 100) : 0) - .gesture(DragGesture().updating($gestureOffset, body: {value, out, _ in - out = value.translation.height - /// onChangeDrawer() updates the offset when a gesture was performed - onChangeDrawer() - }).onEnded({ value in - let maxHeight = height - 100 - /// When the gesture ends, update the placement of the drawer view to fixed position - withAnimation { - if -offset > 100 && -offset < maxHeight / 2 { - offset = -(maxHeight / 3) - } - else if (-offset > maxHeight / 2) { - offset = -maxHeight - } - else { - offset = 0 - } - } - lastOffset = offset - })) - } - .ignoresSafeArea(.all, edges: .bottom) - } - - - /** - onChangeDrawer() resposible for updating the offset when a gesture is performed - */ - func onChangeDrawer (){ - DispatchQueue.main.async { - self.offset = gestureOffset + lastOffset - } - } - /*var searchResults: [ScaffoldingUnit] { - if searchQuery.isEmpty { - return scaffoldingUnits.sorted { $0.name < $1.name } - } else { - return scaffoldingUnits.filter { $0.name.contains(searchQuery) }.sorted { $0.name < $1.name } - } - }*/ - } - - /** - MapViewMap creates a view containing the map - */ - struct MapViewMap: View { - /// Sets the starting location to be Gjøvik (latitude: 60.79574, longitude: 10.69155) - @State var region = MKCoordinateRegion ( - center: CLLocationCoordinate2D( - latitude: 60.79574, - longitude: 10.69155 - ), - /// The zoom level of the application when opened - /// Closer to 0 means greater zoom level - span: MKCoordinateSpan( - latitudeDelta: 0.03, - longitudeDelta: 0.03 - ) - ) - var body: some View { - Map(coordinateRegion: $region) - .ignoresSafeArea() - } - } - - struct MapView_Previews: PreviewProvider { - static var previews: some View { - MapView() - } - } -} - - struct DetailView: View { - var scaffolding: ScaffoldingUnit - - var body: some View { - VStack { - Text(scaffolding.name).font(.title) - - HStack { - Text("\(scaffolding.size) - \(String(format: "%d", scaffolding.amount))") - } - - Spacer() - } - } - } - -struct NavView: View { - @State var searchQuery = "" - - let scaffoldingUnits = [ - ScaffoldingUnit(name: "Spir", size: "2m", amount: 1400), - ScaffoldingUnit(name: "Spir", size: "3m", amount: 1500), - ScaffoldingUnit(name: "Lengdebjelke", size: "3m", amount: 480) - /*"Spir 3m", "Spir 2m", "Bærebjelke", "Trapp", "UTV Trapp", "Bunnskrue", "Diagonalstag DS", "Stillaslem Alu", "AL plank B-230 mm", "Rekkverk", "Enrørsbjelke", "Horisontaler"*/ - ] - - var body: some View { - ZStack { - NavigationView { - List (searchResults) { scaffolding in - NavigationLink(destination: DetailView(scaffolding: scaffolding)) { ScaffoldingUnitRow (scaffolding: scaffolding) } - } - .listStyle(PlainListStyle()) - .searchable(text: $searchQuery) - .navigationTitle("Scaffolding units") - } - .navigationViewStyle(.stack) - } - } - - var searchResults: [ScaffoldingUnit] { - if searchQuery.isEmpty { - return scaffoldingUnits.sorted { $0.name < $1.name } - } else { - return scaffoldingUnits.filter { $0.name.contains(searchQuery) }.sorted { $0.name < $1.name } - } - } - -} - -extension UINavigationController { - override open func viewDidLoad() { - super.viewDidLoad() - - overrideUserInterfaceStyle = .unspecified - - let appearance = UINavigationBarAppearance() - appearance.backgroundColor = UIColor.clear - - navigationBar.standardAppearance = appearance - navigationBar.compactAppearance = appearance - navigationBar.scrollEdgeAppearance = appearance - } -} -*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProfileData.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProfileData.swift new file mode 100644 index 0000000000000000000000000000000000000000..618f05fe0355c1bdf3b23e41dc584eb087c35807 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProfileData.swift @@ -0,0 +1,44 @@ +// +// ProfileData.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 10/05/2022. +// + +import SwiftUI +import Foundation + +/// **ProfileData** +/// Gets the data about the logged in user from the API +class ProfileData: ObservableObject { + /// Is data loading? + @Published private var _isLoadingProfile: Bool = false + + /// Getter for the _isLoadingProfile + var isLoadingProfile: Bool { + get { return _isLoadingProfile} + } + + /// Responsible for getting the data about the logged in user from the API + /// - Parameters: + /// - userID: the userID of the logged in user from Firebase Authentication + /// - completion: completion handler + func loadData(userID: String, completion:@escaping (Profile) -> ()) async { + _isLoadingProfile = true + print("One = \(_isLoadingProfile)") + + guard let url = URL(string: "http://10.212.138.205:8080/stillastracking/v1/api/user?id=\(userID)") else { + print("Invalid url...") + return + } + /// Sends the request and gets the data + URLSession.shared.dataTask(with: url) { [self] data, response, error in + let profile = try! JSONDecoder().decode(Profile.self, from: data!) + DispatchQueue.main.async { + completion(profile) + self._isLoadingProfile = false + print("Two = \(self._isLoadingProfile)") + } + }.resume() + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProjectData.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProjectData.swift new file mode 100644 index 0000000000000000000000000000000000000000..4fc8cd32fa84f409afbfa881043ebe4b969b6e50 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/FetchingApiData/ProjectData.swift @@ -0,0 +1,44 @@ +// +// ProjectData.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 07/04/2022. +// + +import SwiftUI +import Foundation + +/// **ProjectData** +/// Retrieves all the projects and its data from the API +class ProjectData: ObservableObject { + //@Published var projects = [Project]() // TODO: REMOVE IF NOT USED IN THE END + /// Is data loading? + @Published private var _isLoading: Bool = false + + /// Getter for the _isLoading + var isLoading: Bool { + get { return _isLoading} + } + + + /// Responsible for getting the data about projects from the API + /// - Parameter completion: completion handler + func loadData(completion:@escaping ([Project]) -> ()) async { + _isLoading = true + print("One = \(_isLoading)") + + guard let url = URL(string: "http://10.212.138.205:8080/stillastracking/v1/api/project?scaffolding=true") else { + print("Invalid url...") + return + } + /// Sends the request and gets the data + URLSession.shared.dataTask(with: url) { [self] data, response, error in + let projects = try! JSONDecoder().decode([Project].self, from: data!) + DispatchQueue.main.async { + completion(projects) + self._isLoading = false + print("Two = \(self._isLoading)") + } + }.resume() + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/ModelData.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/ModelData.swift deleted file mode 100644 index f88d42b51518fef626ea99ec99adead88f0afed7..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/Model/ModelData.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// ModelData.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 28/03/2022. -// - -import Foundation -import Combine - -/** - ModelData responsible for loading/decoding the user information from the data/json object - To update views when data changes, you make your data model classes observable objects. - */ -final class ModelData: ObservableObject { - @Published var users: [User] = load("userData.json") -} - -/// load<T: Decodable>() - function for decoding the data into json object -/// Uses guard and do/catch methods to make sure to catch potential errors while retreiving or decoding the data -func load<T: Decodable>(_ filename: String) -> T { - let data: Data - - guard let file = Bundle.main.url(forResource: filename, withExtension: nil) - else { - fatalError("Couldn't find \(filename) in main bundle.") - } - - do { - data = try Data(contentsOf: file) - } catch { - fatalError("Couldn't load \(filename) from main bundle:\n\(error)") - } - - do { - let decoder = JSONDecoder() - return try decoder.decode(T.self, from: data) - } catch { - fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/Profile.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/Profile.swift new file mode 100644 index 0000000000000000000000000000000000000000..c8c45a412044c311bf7e17e74a4bec3bf642cb39 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/Profile.swift @@ -0,0 +1,21 @@ +// +// Profile.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 10/05/2022. +// + +import Foundation + +struct Profile: Codable { + let employeeID: String + var name: Name + let dateOfBirth, role: String + let phone: Int + let email: String + let admin: Bool +} + +struct Name: Codable { + let firstName, lastName: String +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/Project.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/Project.swift index b466a92b304949f37447567d367c1302cd9e42da..0d5a17cb6a487a2af73d1c8033f3339bfe140ae0 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/Model/Project.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/Project.swift @@ -8,13 +8,6 @@ import Foundation import SwiftUI -/* -struct Response: Codable{ - var results: [Project] -} - */ - -// MARK: - Project struct Project: Codable { let projectID: Int let projectName: String @@ -28,19 +21,16 @@ struct Project: Codable { let scaffolding: [Scaffolding]? } -// MARK: - Address struct Address: Codable { let street, zipcode, municipality, county: String } -// MARK: - Customer struct Customer: Codable { let name: String let number: Int let email: String } -// MARK: - Geofence struct Geofence: Codable { let wPosition, xPosition, yPosition, zPosition: Position @@ -52,17 +42,14 @@ struct Geofence: Codable { } } -// MARK: - Position struct Position: Codable { let latitude, longitude: Double } -// MARK: - Period struct Period: Codable { let startDate, endDate: String } -// MARK: - Scaffolding struct Scaffolding: Codable { let type: String let quantity: Quantity @@ -73,7 +60,6 @@ struct Scaffolding: Codable { } } -// MARK: - Quantity struct Quantity: Codable { let expected, registered: Int } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/ProjectData.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/ProjectData.swift deleted file mode 100644 index 7a531c183e886fb6aee873967ae075053f468f13..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/Model/ProjectData.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ProjectData.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 07/04/2022. -// - -import SwiftUI -import Foundation - -/** - ProjectData responsible for loading/decoding the project information from the API - To update views when data changes, you make your data model classes observable objects. - */ -class ProjectData: ObservableObject { - @Published var projects = [Project]() - - func loadData(completion:@escaping ([Project]) -> ()) async { - guard let url = URL(string: "http://10.212.138.205:8080/stillastracking/v1/api/project") else { - print("Invalid url...") - return - } - URLSession.shared.dataTask(with: url) { data, response, error in - let projects = try! JSONDecoder().decode([Project].self, from: data!) - DispatchQueue.main.async { - completion(projects) - } - }.resume() - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/Scaffolding.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/Scaffolding.swift deleted file mode 100644 index 270485fb021c9968dc516a339f562c2d9a2f19f5..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/Model/Scaffolding.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// Scaffolding.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 28/03/2022. -// - -import SwiftUI -import Foundation -import CoreLocation -/* -struct Scaffolding: Hashable, Codable, Identifiable { - var id: Int - var type: String - var isFeatured: Bool - - var category: Category - enum Category: String, CaseIterable, Codable { - case flooring = "Flooring" - case spire = "Spire" - case railing = "Railing" - } - - private var imageName: String - var image: Image { - Image(imageName) - } - - var featureImage: Image? { - isFeatured ? Image(imageName + "_feature") : nil - } - - private var coordinates: Coordinates - - var locationCoordinate: CLLocationCoordinate2D { - CLLocationCoordinate2D( - latitude: coordinates.latitude, - longitude: coordinates.longitude - ) - } - - struct Coordinates: Hashable, Codable { - var latitude: Double - var longitude: Double - } - - var batteryLevel: Int -}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/ScaffoldingTransfere.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/ScaffoldingTransfere.swift new file mode 100644 index 0000000000000000000000000000000000000000..fced020bdbab9158a61a32f22f5ceea4789712ba --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/ScaffoldingTransfere.swift @@ -0,0 +1,18 @@ +// +// Scaffolding.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 28/03/2022. +// + +import Combine + +struct Scaff: Codable { + let scaffold: [Move] + let toProjectID, fromProjectID: Int +} + +struct Move: Codable { + let type: String + let quantity: Int +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Model/User.swift b/stillasMobileApplication/stillasMobileApplication/Views/Model/User.swift index 717f0ecb01331fef9df16eb57c17b42647fac486..ad6c33d1604fde689e12390a38c6d011c79515bd 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/Model/User.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/Model/User.swift @@ -8,9 +8,6 @@ import SwiftUI import Foundation -/** - User - decode Data into struct - */ struct User: Codable, Hashable, Identifiable { var id: Int var name: String diff --git a/stillasMobileApplication/stillasMobileApplication/Views/NavigationBarBottom.swift b/stillasMobileApplication/stillasMobileApplication/Views/NavigationBarBottom.swift index 1d78a1479a8aa435f5ae84da92a0e24c806dc0c1..c4e048d871c94e05465428417d72ba60f312f5b9 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/NavigationBarBottom.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/NavigationBarBottom.swift @@ -7,40 +7,44 @@ import SwiftUI +/// **NavigationBarBottom** +/// The navigationbar View responsible for the navigation between the three main views Project, Map and Profile struct NavigationBarBottom: View { - @State private var selection: Tab = .map + /// Sets selection to project + @State private var selection: Tab = .project + + /// All projects @State var projects = [Project]() + + /// Enum for the different pages enum Tab { case project case map case profile - case projectViewAPI } - var body: some View { - TabView(selection: $selection) { - ProjectView() + ProjectListView() .tabItem { - Label("Project", systemImage: "square.grid.2x2") + Label("Prosjekt", systemImage: "square.grid.2x2") } .tag(Tab.project) MapView() .tabItem { - Label("Map", systemImage: "map") + Label("Kart", systemImage: "map") } .tag(Tab.map) ProfileView() .tabItem { - Label("Profile", systemImage: "person.crop.circle") + Label("Profil", systemImage: "person.crop.circle") } .tag(Tab.profile) } .onAppear() { - /// https://www.bigmountainstudio.com/community/public/posts/86559-how-to-customize-the-background-of-the-tabview-in-swiftui + /// The transparrent effect is taken from: https://www.bigmountainstudio.com/community/public/posts/86559-how-to-customize-the-background-of-the-tabview-in-swiftui let appearance = UITabBarAppearance() appearance.backgroundEffect = UIBlurEffect(style: .systemThinMaterial) // Use this appearance when scrolling behind the TabView: @@ -54,6 +58,6 @@ struct NavigationBarBottom: View { struct NavigationBarBottom_Previews: PreviewProvider { static var previews: some View { NavigationBarBottom() - .environmentObject(ModelData()) + //.environmentObject(ModelData()) } } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProfileView/ProfileView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProfileView/ProfileView.swift index 8e21ce68121b45814dafab63c4ac6b04903aac05..ab195642e6b3d88083b5198381ce080f8c9c7f63 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProfileView/ProfileView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProfileView/ProfileView.swift @@ -7,30 +7,28 @@ import SwiftUI -/** - ProfileView - Calls the ProfileDetails view containing information about a user - */ +/// **ProfileView** +/// Calls the ProfileDetails View containing information about a user struct ProfileView: View { var body: some View { VStack { - // TODO: Change input to not take the ModelData's first element only, but get info from API - ProfileDetails(user: ModelData().users[0]) + ProfileDetails() } } } -/** - ProfileDetails - A view responsible for the layout of the user information and showing the details about the user - */ +/// **ProfileDetails** +/// A View responsible for the layout of the user information and showing the details about the user struct ProfileDetails: View { - @EnvironmentObject var modelData: ModelData - - var user: User - - /// Retrieves the user from the json object with ID equal to the object passed into the voew - var userIndex: Int { - modelData.users.firstIndex(where: { $0.id == user.id })! - } + /// Darkmode or lightmode? + @Environment(\.colorScheme) var colorScheme + + /// Models + @EnvironmentObject var viewModel: AppViewModel + @ObservedObject var profileModel: ProfileData = ProfileData() + + /// Initializes a user object + @State var user: [Profile] = [Profile]() var body: some View { ScrollView { @@ -38,47 +36,137 @@ struct ProfileDetails: View { MapView() .ignoresSafeArea(edges: .top) .frame(height: 300) - /// CircleImage responsible for displaying the user profile image - CircleImage(image: user.image) + CircleImage(image: Image("UserProfile")) .offset(y: -130) .padding(.bottom, -130) - /// A VStack used to display all the user profile data - VStack(alignment: .leading) { - HStack { - Text(user.name) - .font(.largeTitle) - } - - HStack { - // TODO: Change to not hard coded values when API is updated - Text("MBStillas") - //.font(.subheadline) - Spacer() - Text("Role: \(user.role)") - //.font(.subheadline) - } - //.font(.subheadline) - .foregroundColor(.secondary) + /// If there are user data + if (!user.isEmpty) { - Divider() + VStack { + VStack { + Image(systemName: "person.crop.circle.badge.checkmark") + .resizable() + .frame(width: 35, height: 30) + .foregroundColor(.blue) + + Text("Bruker info") + .font(Font.system(size: 20).bold()) + .padding(.bottom, 2) + + Text("Nedenfor finner du brukerinformasjonen din.") + .font(.caption) + .foregroundColor(Color.gray) + .padding(.bottom, 5) + } + + VStack { + HStack { + Text("\(user[0].name.firstName) \(user[0].name.lastName)") + } + .font(.title3.bold()) + } + .padding(.bottom, 5) + + VStack { + Text("\(user[0].employeeID)") + .font(.body) + + Text("ANSATT NUMMER") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(user[0].dateOfBirth)") + .font(.body) + + Text("FØDSELSNUMMER") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + VStack { + Text("\(user[0].role)") + .font(.body) + + Text("ROLLE") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(user[0].admin.description)") + .font(.body) + + Text("ADMIN") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + } + .padding() + .frame(width: (UIScreen.screenWidth / 1.2), alignment: .center) + .contentShape(RoundedRectangle(cornerRadius: 5)) + .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + VStack { - Text("Date of birth") - .font(.title2) - Text("\(user.dateOfBirth)") - .foregroundColor(.secondary) + VStack { + Image(systemName: "person.circle") + .resizable() + .frame(width: 30, height: 30) + .foregroundColor(.blue) + + Text("Kontakt info") + .font(Font.system(size: 20).bold()) + .padding(.bottom, 2) + + Text("Nedenfor finner du kontaktinformasjonen din.") + .font(.caption) + .foregroundColor(Color.gray) + .padding(.bottom, 5) + } + + VStack { + Text("\(user[0].phone)") + .font(.body) + + Text("TELEFONNUMMER") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(user[0].email)") + .font(.body) + + Text("EMAIL") + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) } - - Spacer() + .padding() + .frame(width: (UIScreen.screenWidth / 1.2), alignment: .center) + .contentShape(RoundedRectangle(cornerRadius: 5)) + .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + } + } + .task { + /// Get user data from the API with the logged in users ID + await profileModel.loadData(userID: viewModel.userID) { (user) in + self.user.append(user) } - .padding() - - Spacer() } - .navigationTitle(user.name) - .navigationBarTitleDisplayMode(.inline) + .ignoresSafeArea(edges: .top) } } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/AreaFilter/FilterProjectArea.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/AreaFilter/FilterProjectArea.swift index 73444efb81381bdd24e30ab85582f7c9789f9277..98e3bb5ec1136b2aef72a14cd1472cf8403a6007 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/AreaFilter/FilterProjectArea.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/AreaFilter/FilterProjectArea.swift @@ -7,16 +7,26 @@ import SwiftUI +/// **FilterProjectArea** +/// The View for selecting a project with area set to a value struct FilterProjectArea: View { + + /// Is the box checked? @State private var checked: [Bool] + + /// All the selected counties @Binding var selArr: [String] + + /// Area filter active @Binding var areaFilterActive: Bool + /// All counties to filter based of let counties = ["Agder", "Innlandet", "Møre og Romsdal", "Nordland", "Oslo", "Rogaland", "Vestfold og Telemark", "Troms og Finnmark", "Trøndelag", "Vestlandet", "Viken"] - // selectedItems gets updated by the CheckBoxRow as it changes - @State var selectedItems: Set<String> = [] // Use a Set to keep track of multiple check boxes + /// selectedItems gets updated by the CheckBoxRow as it changes + @State var selectedItems: Set<String> = [] /// Use a Set to keep track of multiple check boxes + /// Initializes the selections to false so the boxes are unchecked as the user accesses the filter init(selArr: Binding<[String]>, areaFilterActive: Binding<Bool>) { self._selArr = selArr _checked = State(initialValue: [Bool](repeating: false, count: counties.count)) @@ -27,6 +37,7 @@ struct FilterProjectArea: View { VStack { VStack { List { + /// For each county, add it to the list with a checkbox and description ForEach(counties, id: \.self) { county in HStack { CheckBoxRow(title: county, selectedItems: $selectedItems, isSelected: selectedItems.contains(county)) @@ -40,6 +51,7 @@ struct FilterProjectArea: View { .padding(.bottom, 110) } .overlay(alignment: .bottom) { + /// Updates the parent View with the selected counties Button(action: { print(self.selectedItems) for selectedItem in selectedItems { diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterProjectData.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterProjectData.swift deleted file mode 100644 index 1e62eea9aaf6c92c72b0fc0ce982a55fbd1fde07..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterProjectData.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// FilterProjectData.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 14/04/2022. -// - -import UIKit -import SwiftUI -/* -enum FilterType { - case none, - period, - startBeforePeriod, - startAfterPeriod, - endBeforePeriod, - endAfterPeriod, - sizeEqualTo, - sizeLessThan, - sizeGreaterThan, - state, - county -}*/ - -struct FilterProjectData: View { - @State var projects = [Project]() - @State private var showFilterModalView: Bool = false - @State private var showAddProjectModalView: Bool = false - @State var filterArrArea: [String] = [] - - @State var filter: FilterType = .none - @State var filterArr: [String] = [] - - // TODO: Make these values operable - @State var projectStartDate = Date.distantPast - @State var projectEndDate = Date.distantFuture - @State var projectSize = 99999 - @State var projectState = "Active" - @State var projectCounty = "Innlandet" - - var body: some View { - VStack { - NavigationView { - Form { - Section(header: Text("All Projects")) { - List(filteredProjects, id: \.projectID) { project in - Text(project.projectName) - } - .navigationTitle("Projects") - //.listStyle(.grouped) - } - } - .listStyle(.grouped) - .toolbar { - ToolbarItemGroup(placement: .navigationBarLeading) { - Button(action: { - print("Filter tapped!") - self.showFilterModalView.toggle() - - }) { - Label("Filter", systemImage: "line.3.horizontal.decrease.circle") - } - } - - ToolbarItemGroup(placement: .navigationBarTrailing) { - Button(action: { - print("Add project tapped!") - self.showAddProjectModalView.toggle() - }) { - Label("Add", systemImage: "plus.circle") - } - } - } - .sheet(isPresented: $showFilterModalView, - onDismiss: didDismiss) { - FilterView(selStartDateBind: $projectStartDate, selEndDateBind: $projectEndDate, projectArea: $projectCounty, projectSize: $projectSize, projectStatus: $projectState, filterArr: $filterArr, filterArrArea: $filterArrArea) - .onChange(of: projectStartDate) { value in - filter = .period - } - } - .sheet(isPresented: $showAddProjectModalView, onDismiss: didDismiss) { - AddProjectView() - } - } - } - .task { - await ProjectData().loadData { (projects) in - self.projects = projects - } - } - } - - func didDismiss() { - - // Handle the dismissing action. - } - - var filteredProjects: [Project] { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "dd/MM/yy" - - switch filter { - case .none: - return projects - case .period: - //return projects.filter { $0.period.startDate > projectStartDate && $0.period.endDate < projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! > projectStartDate && dateFormatter.date(from: $0.period.endDate)! < projectEndDate } - case .startBeforePeriod: - //return projects.filter { $0.period.startDate < projectStartDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! < projectStartDate } - case .startAfterPeriod: - //return projects.filter { $0.period.startDate > projectStartDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! > projectStartDate } - case .endBeforePeriod: - //return projects.filter { $0.period.endDate < projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.endDate)! < projectEndDate } - case .endAfterPeriod: - //return projects.filter { $0.period.endDate > projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.endDate)! > projectEndDate } - case .sizeEqualTo: - return projects.filter { $0.size == Int(projectSize) } - case .sizeLessThan: - return projects.filter { $0.size < Int(projectSize) } - case .sizeGreaterThan: - return projects.filter { $0.size > Int(projectSize) } - case .state: - return projects.filter { $0.state == projectState } - case .county: - return projects.filter { $0.address.county == projectCounty } - } - } -} - -struct AddProjectView: View { - var body: some View { - VStack { - Text("Add Project SheetView") - } - } -} -/* -struct FilterProjectData_Previews: PreviewProvider { - static var previews: some View { - FilterProjectData() - } -}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveAreaFilterView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveAreaFilterView.swift index 262b8c9aa5d19796af766ec3a5de3e986a23251c..3f98a295f5187151c11b25ae3b68844de6d0fd70 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveAreaFilterView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveAreaFilterView.swift @@ -7,6 +7,9 @@ import SwiftUI + +/// **ActiveAreaFilterView** +/// The view presented on top of the area-navigation row to display a preview of the selected areafilter. struct ActiveAreaFilterView: View { @Binding var filterArr: [String] @@ -14,36 +17,32 @@ struct ActiveAreaFilterView: View { var body: some View { + /// if there is a filter applied, display a preview of the selected filter if areaFilterActive { HStack { HStack { - //ScrollViewReader { scrollView in - ScrollView (.horizontal, showsIndicators: false) { - HStack { - Text("(\(filterArr.count))") - .padding(.leading, 4) - ForEach(filterArr.indices, id: \.self) { index in - HStack { - Text("\(filterArr[index])") - .lineLimit(1) - .padding(-3) - if index != filterArr.count-1 { - Text(",") - } + ScrollView (.horizontal, showsIndicators: false) { + HStack { + Text("(\(filterArr.count))") + .padding(.leading, 4) + ForEach(filterArr.indices, id: \.self) { index in + HStack { + Text("\(filterArr[index])") + .lineLimit(1) + .padding(-3) + if index != filterArr.count-1 { + Text(",") } } } } - /* - .onAppear { - scrollView.scrollTo(filterArr[filterArr.endIndex]) - } - }*/ + } } .font(.system(size: 11).bold()) .padding(.vertical, 5) .lineLimit(1) + /// Deletes the selected filter and removes it from the preview Button(action: { deleteFilterItem(filterItem: "area") self.areaFilterActive = false @@ -62,6 +61,8 @@ struct ActiveAreaFilterView: View { } } + /// Removes the filter from the array with filters + /// - Parameter filterItem: the selected filter you want to remove func deleteFilterItem(filterItem: String) { if let i = filterArr.firstIndex(of: filterItem) { filterArr.remove(at: i) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActivePeriodFilterView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActivePeriodFilterView.swift index 4f94152c3082cf8face639731661163ce7ee8f0f..c0411c2bec6eddc18136d0efd3ca3bf0a551bd86 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActivePeriodFilterView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActivePeriodFilterView.swift @@ -7,6 +7,8 @@ import SwiftUI +/// **ActivePeriodFilterView** +/// The view presented on top of the size-navigation row to display a preview of the selected periodfilter. struct ActivePeriodFilterView: View { @Binding var startDate: Date @Binding var endDate: Date @@ -17,6 +19,7 @@ struct ActivePeriodFilterView: View { var body: some View { + /// if there is a filter applied, display a preview of the selected filter if periodFilterActive { HStack { HStack { @@ -33,6 +36,7 @@ struct ActivePeriodFilterView: View { .font(.system(size: 11).bold()) .padding(.vertical, 5) + /// Deletes the selected filter and removes it from the preview Button(action: { deleteFilterItem(filterItem: "period") self.periodFilterActive.toggle() @@ -50,6 +54,8 @@ struct ActivePeriodFilterView: View { } } + /// Removes the filter from the array with filters + /// - Parameter filterItem: the selected filter you want to remove func deleteFilterItem(filterItem: String) { if let i = filterArr.firstIndex(of: filterItem) { filterArr.remove(at: i) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveSizeFilterView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveSizeFilterView.swift index e077658847652b7b8d84e58d82f4709714a5b787..a8477ed2756ad105365c980a7fc15bf1f7f50138 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveSizeFilterView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveSizeFilterView.swift @@ -7,6 +7,8 @@ import SwiftUI +/// **ActiveSizeFilterView** +/// The view presented on top of the size-navigation row to display a preview of the selected sizefilter. struct ActiveSizeFilterView: View { @Binding var filterArr: [String] @Binding var projectMinSize: Int @@ -15,12 +17,12 @@ struct ActiveSizeFilterView: View { @Binding var selection: String var body: some View { - + /// if there is a filter applied, display a preview of the selected filter if sizeFilterActive { HStack { HStack { ScrollView (.horizontal, showsIndicators: false) { - if selection == "Between" { + if selection == "Between" { /// display both minimum size and max size HStack { Text("\(projectMinSize) m") + Text("2") @@ -34,7 +36,7 @@ struct ActiveSizeFilterView: View { .font(Font.system(size: 10)) } .padding(.leading, 5) - } else if selection == "Less Than" { + } else if selection == "Less Than" { /// display only minimum size HStack { Text("Under ") + Text("\(projectMinSize) m") @@ -43,7 +45,7 @@ struct ActiveSizeFilterView: View { .font(Font.system(size: 10)) } .padding(.leading, 5) - } else if selection == "Greater Than" { + } else if selection == "Greater Than" { /// display only maximum size HStack { Text("Over ") + Text("\(projectMaxSize) m") @@ -59,6 +61,7 @@ struct ActiveSizeFilterView: View { .padding(.vertical, 5) .lineLimit(1) + /// Deletes the selected filter and removes it from the preview Button(action: { deleteFilterItem(filterItem: "size") self.sizeFilterActive = false @@ -78,6 +81,9 @@ struct ActiveSizeFilterView: View { } } + + /// Removes the filter from the array with filters + /// - Parameter filterItem: the selected filter you want to remove func deleteFilterItem(filterItem: String) { if let i = filterArr.firstIndex(of: filterItem) { filterArr.remove(at: i) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveStatusFilterView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveStatusFilterView.swift index b7a5e9e4e7c3f1df9823363905e992d47ae4417d..deac93f86ff5057782446d10f365fd4b65f56f19 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveStatusFilterView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/ActiveFilterViews/ActiveStatusFilterView.swift @@ -7,13 +7,15 @@ import SwiftUI +/// **ActiveStatusFilterView** +/// The view presented on top of the size-navigation row to display a preview of the selected statusfilter. struct ActiveStatusFilterView: View { @Binding var filterArr: [String] @Binding var projectStatus: String @Binding var statusFilterActive: Bool var body: some View { - + /// if there is a filter applied, display a preview of the selected filter if statusFilterActive { HStack { HStack { @@ -24,6 +26,7 @@ struct ActiveStatusFilterView: View { .font(.system(size: 11).bold()) .padding(.vertical, 5) + /// Deletes the selected filter and removes it from the preview Button(action: { deleteFilterItem(filterItem: "status") self.statusFilterActive.toggle() @@ -41,6 +44,8 @@ struct ActiveStatusFilterView: View { } } + /// Removes the filter from the array with filters + /// - Parameter filterItem: the selected filter you want to remove func deleteFilterItem(filterItem: String) { if let i = filterArr.firstIndex(of: filterItem) { filterArr.remove(at: i) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/FilterView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/FilterView.swift index fffb8620e52ff427e5a49e7f79373d10e062c585..267015dcfb6caa2a56257769a2b21b3b0281c0e7 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/FilterView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/FilterView/FilterView.swift @@ -9,10 +9,13 @@ import SwiftUI // TODO: Add enum for switch case instead of hard-coded values +/// **FilterView** +/// Creates a View displaying the a NavigationView for the different project filters. struct FilterView: View { - // TODO: Add buttons for switching between start before only, period between etc. + /// Filter options @State private var filterItems = ["Område", "Periode", "Størrelse", "Status"] + /// All the different filtervalues @Binding var selStartDateBind: Date @Binding var selEndDateBind: Date @Binding var projectArea: String @@ -20,35 +23,41 @@ struct FilterView: View { @Binding var projectStatus: String @Binding var minProjectSize: Int @Binding var maxProjectSize: Int - // TODO: DENNA TINGEN HER ISTEDENFOR scoreFrom - @Binding var sizeSortType: String + /// Returns whether a filter is active or not @State var periodFilterActive: Bool = false @State var areaFilterActive: Bool = false @State var sizeFilterActive: Bool = false @State var statusFilterActive: Bool = false + /// Array of all the filters active @Binding var filterArr: [String] + + /// All selected areas to be filtered on @Binding var filterArrArea: [String] + /// Selected filter start date and end date @State var selStartDate = Date() @State var selEndDate = Date() var body: some View { NavigationView { List { + /// For each filter type, add it to the List with its respective navigation destination ForEach(filterItems, id: \.self) { filterItem in NavigationLink { switch filterItem { case "Område": FilterProjectArea(selArr: $filterArrArea, areaFilterActive: $areaFilterActive) .onAppear { + /// Resets the filter filterArrArea.removeAll() } case "Periode": FilterProjectPeriod(selStartDateBind: $selStartDate, selEndDateBind: $selEndDate, periodFilterActiveBind: $periodFilterActive) .onAppear { + /// Resets the filter selStartDateBind = Date.distantPast selEndDateBind = Date.distantFuture if (selStartDateBind != Date.distantPast || selEndDateBind != Date.distantFuture) { @@ -75,13 +84,13 @@ struct FilterView: View { FilterProjectStatus(filterArr: $filterArr, selection: $projectStatus) .onChange(of: projectStatus) { status in projectStatus = status - print(projectStatus) statusFilterActive = true } default: Text("No views available") } } label: { + /// Adds label to list as well as adding the filter preview to the list item if it is active HStack { Text(filterItem) Spacer() @@ -132,6 +141,7 @@ struct FilterView: View { .navigationTitle(Text("Filter")) .navigationViewStyle(StackNavigationViewStyle()) .overlay(alignment: .bottom) { + /// Adds and removes active filters based on current selection Button(action: { // TODO: Change to use for loop? /*for filterItem in filterArr { @@ -157,8 +167,6 @@ struct FilterView: View { } else { deleteFilterItem(filterItem: "status") } - - print(filterArr) }) { Text("Bruk") .frame(width: 300, height: 50, alignment: .center) @@ -171,12 +179,17 @@ struct FilterView: View { } } + + /// If the selected filter is newly added and not only updated, add it to the array of filters + /// - Parameter filterItem: the selected filter you want to add func addFilterItem(filterItem: String){ if !filterArr.contains(filterItem) { filterArr.append(filterItem) } } + /// Remove filter item from the array of filters + /// - Parameter filterItem: the selected filter you want to remove func deleteFilterItem(filterItem: String) { if let i = filterArr.firstIndex(of: filterItem) { filterArr.remove(at: i) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/CalendarView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/CalendarView.swift index 11ab17de827f07d62284d420655bba25ec7e52df..a922ae48c29895d6105f5957a3d49789e4610282 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/CalendarView.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/CalendarView.swift @@ -7,11 +7,17 @@ import SwiftUI +/// **CalendarView** +/// Displays the period filter View with two calendars for the user to interact with. struct CalendarView: View { + /// The selected start date and end date @Binding var selStartDate: Date @Binding var selEndDate: Date + + /// Checks if the period filter is active @Binding var periodFilterActive: Bool + /// Base values for the calendar @State private var startDate = Date() @State private var endDate = Date() @@ -21,6 +27,7 @@ struct CalendarView: View { VStack { Section { VStack { + /// First calendar DatePicker( "Start dato", selection: $startDate, @@ -30,6 +37,7 @@ struct CalendarView: View { Divider() + /// Second calendar DatePicker( "Slutt dato", selection: $endDate, @@ -43,14 +51,12 @@ struct CalendarView: View { .padding(.top, 40) } Spacer() + + /// Returns the selected dates to the parent View Button(action: { selStartDate = $startDate.wrappedValue selEndDate = $endDate.wrappedValue periodFilterActive = true - print("______") - print(selStartDate) - print(selEndDate) - print("______") }) { Text("Bruk") .frame(width: 300, height: 50, alignment: .center) diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/FilterProjectPeriod.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/FilterProjectPeriod.swift index fb905174bc0c3826e430137c973aea669dafa20e..31f8debd43c19c1bb8de1d1403e293e6a99f9405 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/FilterProjectPeriod.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/PeriodFilter/FilterProjectPeriod.swift @@ -7,20 +7,27 @@ import SwiftUI +/// **FilterProjectPeriod** +/// The View for selecting a period filter struct FilterProjectPeriod: View { - //@State private var date = Date() + /// Selected start date and end date @Binding var selStartDateBind: Date @Binding var selEndDateBind: Date + + /// Tells if filter is activated or not @Binding var periodFilterActiveBind: Bool @State var periodFilterActive: Bool = true + + /// Initializes the start date and end date to be the date of the day @State private var selStartDate = Date() @State private var selEndDate = Date() - var body: some View { VStack { + /// CalendarView with calendars for both start date and end date CalendarView(selStartDate: $selStartDate, selEndDate: $selEndDate, periodFilterActive: $periodFilterActive) .onAppear { + /// Resets the calendars selected date selStartDateBind = selStartDate selEndDateBind = selEndDate } @@ -34,7 +41,6 @@ struct FilterProjectPeriod: View { } } .navigationTitle(Text("Prosjekt periode")) - //.ignoresSafeArea(edges: .top) } } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeGreaterThanFilter.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeGreaterThanFilter.swift index 3960d4bf85dcff27019a7cbf8e6de8331a8902aa..d108f6965516c925cfac72d8de2912b9753f1faf 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeGreaterThanFilter.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeGreaterThanFilter.swift @@ -7,24 +7,34 @@ import SwiftUI +/// **SizeGreaterThanFilter** +/// The View for selecting a project with size greater than a values struct SizeGreaterThanFilter: View { + + /// Enum for input enum Field: Int, CaseIterable { case input } - + + /// Size filter active @Binding var sizeFilterActive: Bool + /// Has textfield been activated and should be in focus? @FocusState var focusedField: Field? + /// Slider values @State var scoreTo: Int = 1000 @Binding var scoreToBind: Int - + + /// The input of minimum size @ObservedObject var input = NumbersOnly() + /// Slider data var sliderSizeMin = 100.0 var sliderSizeMax = 1000.0 var stepLength = 50.0 + /// Slider value var intProxyS2: Binding<Double>{ Binding<Double>( get: { @@ -43,8 +53,8 @@ struct SizeGreaterThanFilter: View { HStack { VStack { Text("Over") - HStack { + /// Adds textfield with bind to slider TextField("\(Int(sliderSizeMax))", text: $input.value) .font(Font.system(size: 30, design: .default)) .onChange(of: input.value) { value in @@ -92,7 +102,6 @@ struct SizeGreaterThanFilter: View { .font(Font.system(size: 60, design: .default)) VStack { - VStack (alignment: .leading) { HStack { Text("Fra") @@ -102,6 +111,7 @@ struct SizeGreaterThanFilter: View { .font(Font.system(size: 20, design: .default)) .padding(.top, 20) + /// Adds slider for minimum size Slider(value: intProxyS2 , in: sliderSizeMin...sliderSizeMax, step: stepLength, onEditingChanged: {_ in print(scoreTo.description) }) @@ -111,6 +121,8 @@ struct SizeGreaterThanFilter: View { } } Spacer() + + /// Returnerer den brukte størrelsedataen til parent Viewen Button(action: { print("Bruk") scoreTo = Int(input.value) ?? 1000 diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeLessThanFilter.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeLessThanFilter.swift index 2dff321abd2e455ae8c7f6ddd2df73ab7f69f7d6..31fc1b3d7bbe9665fa63c8bd4582bbe65fa41314 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeLessThanFilter.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/BetweenGreaterLess/SizeLessThanFilter.swift @@ -7,23 +7,34 @@ import SwiftUI +/// **SizeLessThanFilter** +/// The View for selecting a project with size less than a values struct SizeLessThanFilter: View { + + /// Enum for input enum Field: Int, CaseIterable { case input } + /// Size filter active @Binding var sizeFilterActive: Bool + /// Has textfield been activated and should be in focus? @FocusState var focusedField: Field? + /// First slider values @State var scoreFrom: Int = 100 @Binding var scoreFromBind: Int + + /// The input of maximum size @ObservedObject var input = NumbersOnly() + /// Slider data var sliderSizeMin = 100.0 var sliderSizeMax = 1000.0 var stepLength = 50.0 + /// Slider value var intProxyS1: Binding<Double>{ Binding<Double>( get: { @@ -42,8 +53,8 @@ struct SizeLessThanFilter: View { HStack { VStack { Text("Under") - HStack { + /// Adds textfield with bind to slider TextField("\(Int(sliderSizeMin))", text: $input.value) .font(Font.system(size: 30, design: .default)) .onChange(of: input.value) { value in @@ -91,6 +102,7 @@ struct SizeLessThanFilter: View { .font(Font.system(size: 20, design: .default)) .padding(.top, 20) + /// Adds slider for maximum size Slider(value: intProxyS1 , in: sliderSizeMin...sliderSizeMax, step: stepLength, onEditingChanged: {_ in print(scoreFrom.description) }) @@ -99,23 +111,25 @@ struct SizeLessThanFilter: View { } } } - Spacer() - Button(action: { - print("Bruk") - scoreFrom = Int(input.value) ?? 100 - scoreFromBind = scoreFrom - sizeFilterActive = true - }) { - Text("Bruk") - .frame(width: 300, height: 50, alignment: .center) + .overlay(alignment: .bottom) { + Spacer() + + /// Returnerer den brukte størrelsedataen til parent Viewen + Button(action: { + print("Bruk") + scoreFrom = Int(input.value) ?? 100 + scoreFromBind = scoreFrom + sizeFilterActive = true + }) { + Text("Bruk") + .frame(width: 300, height: 50, alignment: .center) + } + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(10) + .padding(.bottom, 50) } - .foregroundColor(.white) - //.padding(.vertical, 10) - .background(Color.blue) - .cornerRadius(10) - - Spacer() - .frame(height:50) // limit spacer size by applying a frame + .ignoresSafeArea(.keyboard) } } /* diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/FilterProjectSize.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/FilterProjectSize.swift index 81cd659e47940e17a75991631683192445592143..a0c16563b4106559f028d1b50052de285f09863d 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/FilterProjectSize.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/SizeFilter/FilterProjectSize.swift @@ -7,22 +7,30 @@ import SwiftUI +/// **FilterProjectSize** +/// Responsible for switching between the different filtering Views struct FilterProjectSize: View { + /// Slider values @State var scoreFrom: Int = 100 @State var scoreTo: Int = 1000 + /// Slider selection to be returned to parent View @Binding var scoreFromBind: Int @Binding var scoreToBind: Int + /// Size filter active @Binding var sizeFilterActive: Bool - @Binding var selection: String - let sizeSelections = ["Less Than", "Between", "Greater Than"] + /// The selected size filtering method (Mindre enn, Mellom, Større enn) + @Binding var selection: String + + /// Filtrering metoder + let sizeSelections = ["Mindre enn", "Mellom", "Større enn"] var body: some View { - VStack { VStack { + /// Picker for velging av filtrering metode Picker("Select a state: ", selection: $selection) { ForEach(sizeSelections, id: \.self) { Text($0) @@ -34,13 +42,15 @@ struct FilterProjectSize: View { Spacer() switch selection { - case "Less Than": + case "Mindre enn": + /// Redirects to the SizeLessThanFilter View SizeLessThanFilter(sizeFilterActive: $sizeFilterActive, scoreFrom: scoreFrom, scoreFromBind: $scoreFromBind) .onChange(of: scoreFrom) { val in scoreFromBind = val sizeFilterActive = true } - case "Between": + case "Mellom": + /// Redirects to the SizeBetweenFilter View SizeBetweenFilter(sizeFilterActive: $sizeFilterActive, scoreFrom: scoreFrom, scoreFromBind: $scoreFromBind, scoreTo: scoreTo, scoreToBind: $scoreToBind) .onChange(of: scoreTo) { val in scoreToBind = val @@ -50,7 +60,8 @@ struct FilterProjectSize: View { scoreFromBind = val sizeFilterActive = true } - case "Greater Than": + case "Større enn": + /// Redirects to the SizeGreaterThanFilter View SizeGreaterThanFilter(sizeFilterActive: $sizeFilterActive, scoreTo: scoreTo, scoreToBind: $scoreToBind) .onChange(of: scoreFrom) { val in scoreFromBind = val @@ -71,44 +82,6 @@ extension UIScreen { static let screenSize = UIScreen.main.bounds.size } -struct CornerRadiusStyle: ViewModifier { - var radius: CGFloat - var corners: UIRectCorner - - struct CornerRadiusShape: Shape { - - var radius = CGFloat.infinity - var corners = UIRectCorner.allCorners - - func path(in rect: CGRect) -> Path { - let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) - return Path(path.cgPath) - } - } - - func body(content: Content) -> some View { - content - .clipShape(CornerRadiusShape(radius: radius, corners: corners)) - } -} - -extension View { - func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { - ModifiedContent(content: self, modifier: CornerRadiusStyle(radius: radius, corners: corners)) - } -} - -class NumbersOnly: ObservableObject { - @Published var value = "" { - didSet { - let filtered = value.filter { $0.isNumber } - - if value != filtered { - value = filtered - } - } - } -} /* struct FilterProjectSize_Previews: PreviewProvider { static var previews: some View { diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/StatusFilter/FilterProjectStatus.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/StatusFilter/FilterProjectStatus.swift index 95b182ea262bc3d9e1a87baaea97c91656bf548a..7166c5bba0d6661bdfb85e7ef04bfca5d47f30d9 100644 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/StatusFilter/FilterProjectStatus.swift +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/FilterData/StatusFilter/FilterProjectStatus.swift @@ -7,49 +7,57 @@ import SwiftUI +/// **FilterProjectStatus** +/// The View for selecting a status filter struct FilterProjectStatus: View { + /// Array of all the filters active @Binding var filterArr: [String] + + /// The selected filter @Binding var selection: String - let states = ["Inactive", "Active", "Upcomming"] + /// The different project states + let states = ["Inactive", "Active", "Upcomming"] - var body: some View { - VStack { - Text("Select project status") - .font(.headline) - .padding(.vertical, 5) - - Picker("Select a state: ", selection: $selection) { - ForEach(states, id: \.self) { - Text($0) - } + var body: some View { + VStack { + Text("Select project status") + .font(.headline) + .padding(.vertical, 5) + + /// Picker for choosing a project state + Picker("Select a state: ", selection: $selection) { + ForEach(states, id: \.self) { + Text($0) } - .pickerStyle(SegmentedPickerStyle()) - - HStack { - Text("Selected state: ") - + - Text("\(selection)") - .bold() - - } - .padding(.vertical, 10) + } + .pickerStyle(SegmentedPickerStyle()) + + HStack { + Text("Selected state: ") + + + Text("\(selection)") + .bold() + + } + .padding(.vertical, 10) - Spacer() + Spacer() + } + .navigationTitle(Text("Status")) + .overlay(alignment: .bottom) { + /// Adds the selection and adds filter to filter array + Button(action: { + selection = selection + filterArr.append("status") + }) { + Text("Bruk") + .frame(width: 300, height: 50, alignment: .center) } - .navigationTitle(Text("Status")) - .overlay(alignment: .bottom) { - Button(action: { - selection = selection - filterArr.append("status") - }) { - Text("Bruk") - .frame(width: 300, height: 50, alignment: .center) - } - .foregroundColor(.white) - .background(Color.blue) - .cornerRadius(10) - .padding(.bottom, 50) + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(10) + .padding(.bottom, 50) } } } diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectDetailView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectDetailView.swift deleted file mode 100644 index 5dcb8d6e278a97be870e0100a515f972a9bb76c5..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectDetailView.swift +++ /dev/null @@ -1,107 +0,0 @@ -// -// ProjectDetailView.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 04/04/2022. -// - -import SwiftUI -import MapKit - -struct ProjectDetailView: View { - @Environment(\.colorScheme) var colorScheme - @State private var isShowingSheet = false - - var project: Project - - var body: some View { - ScrollView { - VStack { - /// MapView displaying the map in the top of the screen - MapView() - .frame(height: 300) - - Text("\(project.projectName)") - .font(.title).bold() - .foregroundColor(colorScheme == .dark ? Color(UIColor.darkGray) : Color(UIColor.darkGray)) - - DetailView(project: project) - - Button { - isShowingSheet.toggle() - } label: { - Text("Transfere Scaffolding") - .padding(12) - .font(.system(size: 20)) - .foregroundColor(colorScheme == .dark ? Color(UIColor.black) : Color(UIColor.darkGray)) - } - .contentShape(Rectangle()) - .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) - .sheet(isPresented: $isShowingSheet, - onDismiss: didDismiss) { - TransfereScaffolding() - } - } - } - .ignoresSafeArea(edges: .top) - } - func didDismiss() { - // Handle the dismissing action. - } -} - -struct DetailView: View { - var project: Project - - @Environment(\.colorScheme) var colorScheme - - var body: some View { - let projectInfoTitle = "Project Information" - let duration = "Duration:" - let customer = "Customer:" - let amountScaff = "Amount:" - - VStack { - VStack(alignment: .leading) { - Text(projectInfoTitle) - .font(.title).bold() - - HStack { - Text(duration) - .font(.body).bold() - - Text("\(project.period.startDate) - \(project.period.endDate)") - .font(.body) - } - - HStack { - Text(customer) - .font(.body).bold() - - Text("\(project.customer.name)") - .font(.body) - } - - HStack { - Text(amountScaff) - .font(.body).bold() - - Text("\("ADD INFO TO API")") - .font(.body) - } - } - .foregroundColor(Color(UIColor.darkGray)) - .lineLimit(1) - .layoutPriority(100) - .frame(width: 350, height: 125) - .background(colorScheme == .dark ? Color.white : Color(UIColor.white)) - .cornerRadius(15) - .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) - .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) - .overlay( - RoundedRectangle(cornerRadius: 15) - .stroke(colorScheme == .dark ? Color.gray.opacity(0.1) : Color.gray.opacity(0.1), lineWidth: 1) - ) - } - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView.swift deleted file mode 100644 index d469dbcaa959e24d811252751036a8a2cea16681..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView.swift +++ /dev/null @@ -1,230 +0,0 @@ -// -// ProjectView.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 28/03/2022. -// - -import SwiftUI -import UIKit -/* -struct BookMark: Identifiable { - let id = UUID() - let name: String - let icon: String - var items: [BookMark]? -} - -struct ProjectView1: View { - let items: [BookMark] = [.example1, .example2, .example3] - var body: some View { - Section(header: Text("Second List")) { - List(items, children: \.items) { row in - Image(systemName: row.icon) - Text(row.name) - } - } - } -}*/ - - -enum FilterType { - case none, - period, - startBeforePeriod, - startAfterPeriod, - endBeforePeriod, - endAfterPeriod, - sizeBetween, - sizeEqualTo, - sizeLessThan, - sizeGreaterThan, - state, - county -} - -struct ProjectRow: View { - var project: Project - - var body: some View { - HStack { - VStack(alignment: .leading) { - Text(project.projectName).font(.headline) - Text(project.period.startDate + " until " + project.period.endDate).font(.subheadline).foregroundColor(.gray) - } - Spacer() - Text(String(format: "%d", project.projectID)) - .foregroundColor(.gray) - } - } -} - -/* -extension BookMark { - static let apple = BookMark(name: "Apple", icon: "1.circle") - static let bbc = BookMark(name: "BBC", icon: "square.and.pencil") - static let swift = BookMark(name: "Swfit", icon: "bolt.fill") - static let twitter = BookMark(name: "Twitter", icon: "mic") - - static let example1 = BookMark(name: "Favorites", icon: "star", items: [BookMark.apple, BookMark.bbc, BookMark.swift, BookMark.twitter]) - static let example2 = BookMark(name: "Recent", icon: "timer", items: [BookMark.apple, BookMark.bbc, BookMark.swift, BookMark.twitter]) - static let example3 = BookMark(name: "Recommended", icon: "hand.thumbsup", items: [BookMark.apple, BookMark.bbc, BookMark.swift, BookMark.twitter]) -} -*/ - -struct ProjectView: View { - @State var searchQuery = "" - @State var hasFetchedData = false - @State var projects = [Project]() - @State private var showFilterModalView: Bool = false - - @State var sizeSortType: String = "Between" - @State var filter: FilterType = .none - @State var filterArr: [String] = [] - @State var filterArrArea: [String] = [] - - // TODO: REMOVE? - @State var projectStartDate = Date.distantPast - @State var projectEndDate = Date.distantFuture - @State var projectSize = 99999 - @State var minProjectSize = 100 - @State var maxProjectSize = 1000 - @State var projectState = "Active" - @State var projectCounty = "Innlandet" - - var body: some View { - VStack { - NavigationView { - Form { - Section(header: Text("All Projects")) { - /*List(searchResults, id: \.projectID) { project in - NavigationLink(destination: ProjectDetailView(project: project), label: { - ProjectRow(project: project) } - ) - } - .navigationTitle("Projects")*/ - List(searchResults, id: \.projectID) { project in - NavigationLink(destination: ProjectDetailView(project: project), label: { - ProjectRow(project: project) } - ) - } - .navigationTitle("Projects") - //.listStyle(.grouped) - } - } - .listStyle(.grouped) - .toolbar { - ToolbarItemGroup(placement: .navigationBarLeading) { - Button(action: { - print("Filter tapped!") - self.showFilterModalView.toggle() - }) { - Label("Filter", systemImage: "line.3.horizontal.decrease.circle") - } - } - - ToolbarItemGroup(placement: .navigationBarTrailing) { - Button(action: { - print("Add project tapped!") - }) { - Label("Add", systemImage: "plus.circle") - } - } - } - } - .task { - await ProjectData().loadData { (projects) in - self.projects = projects - } - } - .sheet(isPresented: $showFilterModalView, - onDismiss: didDismiss) { - FilterView(selStartDateBind: $projectStartDate, selEndDateBind: $projectEndDate, projectArea: $projectCounty, projectSize: $projectSize, projectStatus: $projectState, minProjectSize: $minProjectSize, maxProjectSize: $maxProjectSize, sizeSortType: $sizeSortType, filterArr: $filterArr, filterArrArea: $filterArrArea) - .onChange(of: filterArr) { filterVal in - if filterArr.contains("period") { - filter = .period - } - if filterArr.contains("area") { - filter = .county - } - if filterArr.contains("size") && sizeSortType == "Between" { - filter = .sizeBetween - } else if filterArr.contains("size") && sizeSortType == "Less Than" { - filter = .sizeLessThan - } else if filterArr.contains("size") && sizeSortType == "Greater Than" { - filter = .sizeGreaterThan - } - if filterArr.contains("status") { - filter = .state - } - if filterArr.isEmpty { - filter = .none - } - } - } - .navigationViewStyle(.stack) - .searchable(text: $searchQuery, placement: .navigationBarDrawer(displayMode: .always)) - .refreshable { - print("Refreshed") - // TODO: Add action here (retrieve from API again or something) - } - //ProjectView1() - } - } - - // TODO: Add support for search of filtered items - var searchResults: [Project] { - if searchQuery.isEmpty { - return filteredProjects.sorted { $0.projectName < $1.projectName } - } else { - return filteredProjects.filter { $0.projectName.lowercased().contains(searchQuery.lowercased()) }.sorted { $0.projectName < $1.projectName } - } - } - - func didDismiss() { - // Handle the dismissing action. - } - - var filteredProjects: [Project] { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "dd/MM/yyyy" - - switch filter { - case .none: - return projects - case .period: - //return projects.filter { $0.period.startDate > projectStartDate && $0.period.endDate < projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! >= projectStartDate && dateFormatter.date(from: $0.period.endDate)! <= projectEndDate } - case .startBeforePeriod: - //return projects.filter { $0.period.startDate < projectStartDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! <= projectStartDate } - case .startAfterPeriod: - //return projects.filter { $0.period.startDate > projectStartDate } - return projects.filter { dateFormatter.date(from: $0.period.startDate)! >= projectStartDate } - case .endBeforePeriod: - //return projects.filter { $0.period.endDate < projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.endDate)! <= projectEndDate } - case .endAfterPeriod: - //return projects.filter { $0.period.endDate > projectEndDate } - return projects.filter { dateFormatter.date(from: $0.period.endDate)! >= projectEndDate } - case .sizeBetween: - return projects.filter { $0.size >= Int(minProjectSize) && $0.size <= Int(maxProjectSize)} - case .sizeEqualTo: - return projects.filter { $0.size == Int(projectSize) } - case .sizeLessThan: - return projects.filter { $0.size < Int(minProjectSize) } - case .sizeGreaterThan: - return projects.filter { $0.size > Int(maxProjectSize) } - case .state: - return projects.filter { $0.state == projectState } - case .county: - return projects.filter { filterArrArea.contains($0.address.county)} - } - } -} - -struct Project_Previews: PreviewProvider { - static var previews: some View { - ProjectView() - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/HistoryOfScaffolding.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/HistoryOfScaffolding.swift new file mode 100644 index 0000000000000000000000000000000000000000..4b4b982501a8c8459f09f2f0c0c6f5b00af9d744 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/HistoryOfScaffolding.swift @@ -0,0 +1,232 @@ +// +// HistoryOfScaffolding.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 09/05/2022. +// + +import SwiftUI + +/// **HistoryOfScaffolding** +/// A View for showing the history of a scaffolding type for a project +/// As of now, it is hard coded due to missing implementation in the API, however, the design and concept is the same. +struct HistoryOfScaffolding: View { + /// All projects + var projects: [Project] + + /// Darkmode or lightmode activated? + @Environment(\.colorScheme) var colorScheme + + /// Scaffolding type + var scaffolding: Scaffolding + + /// Transfere scaffolding Modal View showing + @Binding var isShowingSheet: Bool + + /// Used to show dates in history + let dateNow = Date() + + var body: some View { + ScrollView(.vertical) { + VStack (alignment: .leading){ + HStack { + VStack { + /// Each "blob" with history info + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: -2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: -10, y: 10) + .overlay(VStack { + /// Preview data for scaffolding on the project on a given date + Text(Date(), style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + /// Changes color of registered depending on how close it is to be equal to expected + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .padding(.leading) + .padding(.vertical, 35) + .ignoresSafeArea() + + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: -2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: -10, y: 10) + .overlay(VStack { + /// Sets date to go back in time for each history "blob" + Text(Calendar.current.date(byAdding: .day, value: -2, to: dateNow)!, style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .padding(.leading) + .padding(.vertical, 35) + .ignoresSafeArea() + + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: -2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: -10, y: 10) + .overlay(VStack { + Text(Calendar.current.date(byAdding: .day, value: -4, to: dateNow)!, style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .padding(.leading) + .padding(.vertical, 35) + .ignoresSafeArea() + } + + Divider() + .frame(width: 3, height: 400, alignment: .center) + .background(Color.gray) + .padding(.top) + .offset(y: 30) + + VStack { + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 10, y: 10) + .overlay(VStack { + Text(Calendar.current.date(byAdding: .day, value: -1, to: dateNow)!, style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + .font(.system(size: 15)) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .offset(y: 70) + .padding(.trailing) + .padding(.vertical, 35) + .ignoresSafeArea() + + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 10, y: 10) + .overlay(VStack { + Text(Calendar.current.date(byAdding: .day, value: -3, to: dateNow)!, style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + .font(.system(size: 15)) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .offset(y: 70) + .padding(.trailing) + .padding(.vertical, 35) + .ignoresSafeArea() + + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .frame(width: UIScreen.screenWidth / 2.3 , height: 60) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 2, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 10, y: 10) + .overlay(VStack { + Text(Calendar.current.date(byAdding: .day, value: -5, to: dateNow)!, style: .date) + .foregroundColor(Color.gray) + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + .font(.system(size: 15)) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + } + }) + .offset(y: 70) + .padding(.trailing) + .padding(.vertical, 35) + .ignoresSafeArea() + } + } + } + } + .navigationTitle(Text("Historie for \(scaffolding.type)".capitalizingFirstLetter())) + } +} + +/* +struct HistoryOfScaffolding_Previews: PreviewProvider { + static var previews: some View { + HistoryOfScaffolding() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoDetailedView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoDetailedView.swift new file mode 100644 index 0000000000000000000000000000000000000000..30cab034ff95435faea6c1cf5ffbfa3507ae64c2 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoDetailedView.swift @@ -0,0 +1,179 @@ +// +// ProjectInfoDetailedView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 29/04/2022. +// + +import SwiftUI +import UIKit + +struct ProjectInfoDetailedView: View { + /// Selected project + var project: Project + + /// Darkmode or lightmode? + @Environment(\.colorScheme) var colorScheme + + /// Predefined to be easily maintianed and changed + let duration = "VARIGHET" + let customer = "KUNDE" + let amountScaff = "MENGDE" + let scaffoldingSize = "STØRRELSE" + let state = "STATUS" + let address = "ADRESSE" + let contactPerson = "KONTAKT PERSON" + let phoneNumber = "MOBILNUMMER" + let email = "EMAIL" + + var body: some View { + VStack { + VStack(alignment: .leading) { + /// Project info card + VStack(alignment: .leading) { + VStack { + VStack { + Image(systemName: "square.text.square") + .resizable() + .frame(width: 30, height: 30) + .foregroundColor(.blue) + + Text("Prosjekt info") + .font(Font.system(size: 20).bold()) + .padding(.bottom, 2) + + Text("Nedenfor finner du informasjon om dette prosjektet.") + .font(.caption) + .foregroundColor(Color.gray) + .padding(.bottom, 5) + } + + VStack { + Text("\(project.customer.name)") + .font(.body) + + Text(customer) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack(alignment: .leading) { + VStack { + Text("\(project.period.startDate) - \(project.period.endDate)") + .font(.body) + + Text(duration) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + } + .padding(.bottom, 5) + + VStack { + HStack { + Text("\(project.size) m") + + Text("2") + .baselineOffset(6) + .font(.system(size: 12)) + } + .font(.body) + + Text(scaffoldingSize) + .foregroundColor(.gray) + .font(.system(size: 15)) + .padding(.bottom, 5) + } + + VStack { + Text("\(project.state)") + .font(.body) + + Text(state) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + } + } + .padding() + .frame(width: (UIScreen.screenWidth / 1.2), alignment: .center) + .contentShape(RoundedRectangle(cornerRadius: 5)) + .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + + /// Contact info card + VStack { + VStack { + Image(systemName: "person.circle") + .resizable() + .frame(width: 30, height: 30) + .foregroundColor(.blue) + + Text("Kontakt info") + .font(Font.system(size: 20).bold()) + .padding(.bottom, 2) + + Text("Nedenfor finner du kontaktinformasjonen til kunden.") + .font(.caption) + .foregroundColor(Color.gray) + .padding(.bottom, 5) + } + + VStack { + Text("\(project.customer.name)") + .font(.body) + + Text(contactPerson) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(project.address.street), \(project.address.zipcode) \(project.address.municipality)") + .font(.body) + + Text(address) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(project.customer.number)") + .font(.body) + + Text(phoneNumber) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + .padding(.bottom, 5) + + VStack { + Text("\(project.customer.email)") + .font(.body) + + Text(email) + .foregroundColor(.gray) + .font(.system(size: 15)) + } + } + .padding() + .frame(width: (UIScreen.screenWidth / 1.2), alignment: .center) + .contentShape(RoundedRectangle(cornerRadius: 5)) + .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + } + } + .padding(.horizontal, 20) + } +} + +/* +struct ProjectInfoDetailedView_Previews: PreviewProvider { + static var previews: some View { + ProjectInfoDetailedView() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoView.swift new file mode 100644 index 0000000000000000000000000000000000000000..da4da0f60dbee413d612eef8b6c01e739f01d427 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ProjectInfoView.swift @@ -0,0 +1,71 @@ +// +// ProjectDetailView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 04/04/2022. +// + +import SwiftUI +import MapKit + +/// **ProjectInfoView** +/// The View responsible for displaying the project info and scaffolding info Views +struct ProjectInfoView: View { + /// Darkmode or light mode? + @Environment(\.colorScheme) var colorScheme + + /// Transfere scaffolding Modal View showing? + @State private var isShowingSheet = false + + /// All projects + var projects: [Project] + + /// Specific project + var project: Project + + /// The two views available + let siteSelections = ["Stillas", "Prosjekt Info"] + + /// Initialize selection + @State var selection: String = "Prosjekt Info" + + var body: some View { + ScrollView { + VStack { + /// MapView displaying the map in the top of the screen + MapView() + .frame(height: 300) + + Text("\(project.projectName)") + .font(.title).bold() + .foregroundColor(colorScheme == .dark ? Color(UIColor.darkGray) : Color(UIColor.darkGray)) + + VStack { + Picker("Select a state: ", selection: $selection) { + ForEach(siteSelections, id: \.self) { + Text($0) + } + } + .pickerStyle(SegmentedPickerStyle()) + .padding(.bottom, 15) + + Spacer() + + switch selection { + case "Prosjekt Info": + VStack { + ProjectInfoDetailedView(project: project) + } + case "Stillas": + VStack { + ScaffoldingView(projects: projects, scaffolding: project.scaffolding!) + } + default: + Text("Found none") + } + } + } + } + .ignoresSafeArea(edges: .top) + } +} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingDetailedView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingDetailedView.swift new file mode 100644 index 0000000000000000000000000000000000000000..954324068013f0def84efe2cf85702df7797c97f --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingDetailedView.swift @@ -0,0 +1,39 @@ +// +// ScaffoldingDetailedView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 09/05/2022. +// + +import SwiftUI + +/// **ScaffoldingDetailedView** +/// The detailed view of each scaffolding type +/// Snowes history of the type as well as a button which redirects to transfering of scaffolding +struct ScaffoldingDetailedView: View { + + /// All projects used in transfere scaffolding + var projects: [Project] + + /// The scaffolding type + var scaffolding: Scaffolding + + /// Transfere scaffolding Modal View is showing + @Binding var isShowingSheet: Bool + + var body: some View { + VStack { + /// History of scaffolding unit type for project and transfere scaffolding button + HistoryOfScaffolding(projects: projects, scaffolding: scaffolding, isShowingSheet: $isShowingSheet) + + TransfereScaffoldingButton(projects: projects, scaffolding: scaffolding, isShowingSheet: $isShowingSheet) + } + } +} + +/* +struct ScaffoldingDetailedView_Previews: PreviewProvider { + static var previews: some View { + ScaffoldingDetailedView() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItem.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItem.swift new file mode 100644 index 0000000000000000000000000000000000000000..e782b36c4ee19fc1d3ad636792b7ed3603b18ef8 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItem.swift @@ -0,0 +1,101 @@ +// +// ScaffoldingItem.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 01/05/2022. +// + +import SwiftUI + +/// **ScaffoldingItem** +/// The preview used for the scaffolding buttons with scaffolding preview data +struct ScaffoldingItem: View { + /// Checks if the device has light- or dark mode activated + @Environment(\.colorScheme) var colorScheme + + /// The scaffolding unit + var scaffolding: Scaffolding + + var body: some View { + VStack { + /// Preview data for the button in use + Text("\(scaffolding.type)".capitalizingFirstLetter()).font(.title2) + .lineLimit(1) + + Image("\(scaffolding.type)".capitalizingFirstLetter()).resizable().scaledToFit() + + Spacer() + + HStack { + VStack { + Text(String(format: "%d", scaffolding.quantity.expected)).foregroundColor(.black) + .font(.system(size: 15)) + Text("FORVENTET") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + .frame(alignment: .center) + + VStack { + amountOfScaffoldingRegistered(expected: scaffolding.quantity.expected, registered: scaffolding.quantity.registered) + Text("REGISTRERT") + .foregroundColor(.gray) + .font(.system(size: 10)) + } + .frame(alignment: .center) + } + } + .padding(.vertical, 5) + .frame(width: (UIScreen.screenWidth / 2) - 40, height: (UIScreen.screenWidth / 2) - 40, alignment: .center) + .contentShape(RoundedRectangle(cornerRadius: 5)) + .background(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + } +} + + +/// A method for checking the registered scaffolding up against the expected scaffolding and assigning color to the result text depending on how close they are to be equal to eachother +/// - Parameters: +/// - expected: The expected amount of the scaffolding type for the project +/// - registered: The registered amount of the scaffolding type for the project at a given time +/// - Returns: The text of registered units with either red, yellow, green or purple color depending on count +func amountOfScaffoldingRegistered(expected: Int, registered: Int) -> Text { + if (registered >= Int(Double(expected) * 0.95) && registered <= Int(Double(expected))) { + return Text(String(format: "%d", registered)).foregroundColor(Color.green) + .font(.system(size: 15)) + } else if ((registered < Int(Double(expected) * 0.95)) && (registered >= Int(Double(expected) * 0.8))) { + return Text(String(format: "%d", registered)).foregroundColor(Color.yellow) + .font(.system(size: 15)) + } else if (registered > Int(Double(expected))) { + return Text(String(format: "%d", registered)).foregroundColor(Color.purple) + .font(.system(size: 15)) + } else { + return Text(String(format: "%d", registered)).foregroundColor(Color.red) + .font(.system(size: 15)) + } +} + +/// An extension to the String struct which adds a function for capitalizing the first letter in a word. +/// This was taken in use from the following URL: +/// https://www.hackingwithswift.com/example-code/strings/how-to-capitalize-the-first-letter-of-a-string +extension String { + + /// Capitalizes the first letter of a word + /// - Returns: The word with the capitalized first letter + func capitalizingFirstLetter() -> String { + return prefix(1).capitalized + dropFirst() + } + + mutating func capitalizeFirstLetter() { + self = self.capitalizingFirstLetter() + } +} + +/* +struct ScaffoldingItem_Previews: PreviewProvider { + static var previews: some View { + ScaffoldingItem() + } +} +*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItems.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItems.swift new file mode 100644 index 0000000000000000000000000000000000000000..cd420009dcb87a83e6346d9fafabd91b22fd3c63 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingItems.swift @@ -0,0 +1,46 @@ +// +// ScaffoldingItems.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 01/05/2022. +// + +import SwiftUI + +/// **ScaffoldingItems** +/// A grid containing all the scaffolding items as buttons +struct ScaffoldingItems: View { + /// Projects needed for transfering scaffolding + var projects: [Project] + + /// A two-column grid layout which is flexible + var gridItemLayout = [GridItem(.flexible()), GridItem(.flexible())] + + /// All scaffolding items + var scaffolding: [Scaffolding] + + /// Modal View of transfere scaffolding + @Binding var isShowingSheet: Bool + + var body: some View { + ScrollView (.vertical) { + LazyVGrid (columns: gridItemLayout, spacing: 10) { + /// For each scaffolding type on the project, add scaffolding button + ForEach(scaffolding, id: \.type) { scaffolding in + NavigationLink(destination: ScaffoldingDetailedView(projects: projects, scaffolding: scaffolding, isShowingSheet: $isShowingSheet), + label: { ScaffoldingItem(scaffolding: scaffolding) + }) + .listStyle(.grouped) + } + } + .padding(.vertical, 20) + } + } +} + +/* +struct ScaffoldingItems_Previews: PreviewProvider { + static var previews: some View { + ScaffoldingItems() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingView.swift new file mode 100644 index 0000000000000000000000000000000000000000..9f4fa8b709ebb451dd4c1c6e2ba7f6d9a68f2cbd --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/ScaffoldingView.swift @@ -0,0 +1,32 @@ +// +// ScaffoldingView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 29/04/2022. +// + +import SwiftUI + +/// **ScaffoldingView** +/// The view responsible for showing scaffolding units +struct ScaffoldingView: View { + var projects: [Project] + + /// All scaffolding units + var scaffolding: [Scaffolding] + + /// Scaffolding item Modal View + @State var isShowingSheet: Bool = false + var body: some View { + VStack { + ScaffoldingItems(projects: projects, scaffolding: scaffolding, isShowingSheet: $isShowingSheet) + } + } +} + +/* +struct ScaffoldingView_Previews: PreviewProvider { + static var previews: some View { + ScaffoldingView() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffolding.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffolding.swift new file mode 100644 index 0000000000000000000000000000000000000000..813f99cd8af7cdbd6c1ac9af27d2459c6cc23e40 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffolding.swift @@ -0,0 +1,45 @@ +// +// TransfereScaffolding.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 05/04/2022. +// + +import SwiftUI +import Foundation + +/// **TrandsfereScaffolding** +/// Background view of TransfereScaffolding +struct TransfereScaffolding: View { + /// All projects used for transfering + var projects: [Project] + + /// Lightmode or darkmode? + @Environment(\.colorScheme) var colorScheme + + /// Scaffolding type + var scaffolding: Scaffolding + + /// Transfere Modal View is showing + @Binding var isShowingSheet: Bool + + var body: some View { + VStack { + /// Transfere scaffolding view + TransfereScaffoldingView(isShowingSheet: $isShowingSheet, projects: projects, scaffolding: scaffolding) + .navigationTitle(Text("Overfør \(scaffolding.type)")) + } + } + + /// Modal view dismissed + func didDismiss() { + // Handle the dismissing action. + } +} + +/* +struct TransfereScaffolding_Previews: PreviewProvider { + static var previews: some View { + TransfereScaffolding() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingButton.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingButton.swift new file mode 100644 index 0000000000000000000000000000000000000000..1a061eb4865d103182abf2b0dfecee58bd7e86ef --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingButton.swift @@ -0,0 +1,64 @@ +// +// TransfereScaffoldingButton.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 01/05/2022. +// + +import SwiftUI + +/// **TransfereScaffoldingButton** +/// Button for activating of transfere scaffolding Modal View +struct TransfereScaffoldingButton: View { + /// All projects + var projects: [Project] + + /// Darkmode or lightmode activated? + @Environment(\.colorScheme) var colorScheme + + /// Specific scaffolding type + var scaffolding: Scaffolding + + /// Transfere scaffolding Modal View is showing + @Binding var isShowingSheet: Bool + + var body: some View { + /// Button for opening transfere scaffolding Modal View + Button { + isShowingSheet.toggle() + } label: { + Text("Overfør Stillas") + .padding(12) + .font(.system(size: 20)) + .foregroundColor(colorScheme == .dark ? Color(UIColor.white) : Color(UIColor.white)) + .frame(width: 300, height: 50, alignment: .center) + } + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(10) + .padding(.bottom, 50) + + .contentShape(Rectangle()) + //.background(colorScheme == .dark ? Color.blue : Color.blue).cornerRadius(7) + .shadow(color: Color(UIColor.black).opacity(0.1), radius: 5, x: 0, y: 2) + .shadow(color: Color(UIColor.black).opacity(0.2), radius: 20, x: 0, y: 10) + .sheet(isPresented: $isShowingSheet, + onDismiss: didDismiss) { + /// Transfere scaffolding Modal View + TransfereScaffolding(projects: projects, scaffolding: scaffolding, isShowingSheet: $isShowingSheet) + } + } + + /// Modal view got dismissed + func didDismiss() { + // Handle the dismissing action. + } +} + +/* +struct TransfereScaffoldingButton_Previews: PreviewProvider { + static var previews: some View { + TransfereScaffoldingButton() + } +} +*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingView.swift new file mode 100644 index 0000000000000000000000000000000000000000..49d8aff8bbcf741a3f6a90443c1df936f8d32df7 --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectDetailView/TransfereScaffoldingView.swift @@ -0,0 +1,291 @@ +// +// TransfereScaffoldingView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 09/05/2022. +// + +import SwiftUI +import Combine + +/// **TransfereScaffoldingView** +/// The View for transfering scaffolding +struct TransfereScaffoldingView: View { + /// Input field + enum Field: Int, CaseIterable { + case input + } + + /// Is Modal View active + @Binding var isShowingSheet: Bool + + /// Field is selected --> Focus + @FocusState var focusedField: Field? + + /// All projects + var projects: [Project] + + /// Scaffolding type + var scaffolding: Scaffolding + + /// Value of project picker selection + @State private var pickerSelectionFrom: String = "" + @State private var pickerSelectionTo: String = "" + + /// Checks if "from project" is not selected yet + @State private var pickerSelectionFromNotPicked: Bool = true + + /// Search for projects + @State private var searchTerm: String = "" + + /// Is amount of scaffolding empty? + @State private var empty = false + + /// The number of scaffolding (as string because of textfield) + @State var anumber: String = "" + + /// Template numbers to be picked from + var commonNumbers: [String] = ["1", "5", "10", "25", "50"] + + @State var selectedIndex: Int = -1 + @State private var confirmationMessage = "" + @State private var confirmationTitle = "" + @State private var showingConfirmation = false + @State private var transfereMessage = "" + @State private var transfereConfirmation: Bool = false + + /// ProjectID's + @State var pickedFromID: Int = 0 + @State var pickedToID: Int = 0 + + @State var tempIntFrom: Int = 0 + @State var tempIntTo: Int = 0 + + /// Checks the expected count of the selected project + @State var tem: Int = 0 + + /// The filtered projects + var filteredProjects: [Project] { + projects.filter { + searchTerm.isEmpty ? true : $0.projectName.lowercased().contains(searchTerm.lowercased()) + } + } + + var body: some View { + NavigationView { + Form { + Section(header: Text("Fra prosjekt")) { + /// Picker for the project to transfere scaffolding from + Picker(selection: $pickerSelectionFrom, label: Text("Fra prosjekt")) { + SearchBar(text: $searchTerm, placeholder: "Søk etter prosjekt") + .onAppear { + searchTerm = "" + /// Reset number of scaffolding + anumber.removeAll() + } + .onChange(of: pickerSelectionFrom) { pick in + tempIntFrom = projects.firstIndex(where: { $0.projectName == pick })! + pickedFromID = projects[tempIntFrom].projectID + pickerSelectionFromNotPicked = false + } + /// List all projects + ForEach(filteredProjects, id: \.projectID) { project in + Text("\(project.projectName)").tag("\(project.projectName)") + } + } + .onAppear{ + if pickerSelectionFrom.isEmpty { + // TODO: Fix border and error handling here + } + } + } + + Section(header: Text("Til prosjekt")) { + /// Picker for the project to transfere scaffolding to + Picker(selection: $pickerSelectionTo, label: Text("Til prosjekt")) { + SearchBar(text: $searchTerm, placeholder: "Søk etter prosjekt") + .onAppear { + searchTerm = "" + } + .onChange(of: pickerSelectionTo) { pick in + tempIntTo = projects.firstIndex(where: { $0.projectName == pick })! + pickedToID = projects[tempIntTo].projectID + } + /// List all projects + ForEach(filteredProjects, id: \.projectID) { project in + Text("\(project.projectName)").tag("\(project.projectName)") + } + } + } + + Section (header: Text("Antall \(scaffolding.type)")){ + VStack(alignment: .leading) { + Text("Forhåndsdefinert mengde") + /// Picker for the predefined scaffolding amount + Picker("Pick a number", selection: $anumber) { + ForEach(commonNumbers, id: \.self) { aNumber in + Image(systemName: "\(aNumber).circle.fill") + } + } + .pickerStyle(SegmentedPickerStyle()) + } + .padding() + + HStack { + Text("Manuell innfylling") + TextField("Input", text: $anumber) + .disabled(pickerSelectionFromNotPicked) + //.modifier(ClearButton(text: $anumber)) + .onChange(of: anumber) { + tem = (projects[tempIntFrom].scaffolding![projects[tempIntFrom].scaffolding!.firstIndex(where: { $0.type == scaffolding.type}) ?? 0].quantity.expected) + + if $0.isEmpty || Int($0) ?? -1 > tem { + empty = true + } else { + empty = false + } + } + .textFieldStyle(TextFieldEmpty(empty: $empty)) + .keyboardType(.numberPad) + .focused($focusedField, equals: .input) + /// Code taken from: + /// https://stackoverflow.com/questions/58733003/how-to-create-textfield-that-only-accepts-numbers + /// to make sure input is of type Int and update picker + .onReceive(Just(anumber)) { newValue in + let filtered = newValue.filter { "0123456789".contains($0) } + if filtered != newValue { + self.anumber = filtered + } + } + .toolbar { + ToolbarItem(placement: .keyboard) { + Button("Done") { + focusedField = nil + } + } + } + } + .padding() + } + } + .overlay(alignment: .bottom) { + Spacer() + /// Button to start transaction for transfering scaffolding + Button(action: { + Task { + if !empty { + /// Starts transfere process if alert is confirmed + transfereMessage = "Overfør \(Int(anumber)!)x \(scaffolding.type) fra \(pickerSelectionFrom) til \(pickerSelectionTo)?" + transfereConfirmation = true + } + } + }) { + Text("Bruk") + .frame(width: 300, height: 50, alignment: .center) + } + .foregroundColor(.white) + .background(Color.blue) + .cornerRadius(10) + .padding(.bottom, 50) + } + .ignoresSafeArea(.keyboard) + .navigationTitle(Text("Overfør \(scaffolding.type)")) + .alert(isPresented: $showingConfirmation) { + /// Transfere was successful alert + Alert( + title: Text(confirmationTitle), + message: Text(confirmationMessage), + dismissButton: .default(Text("OK")) { + isShowingSheet = false + showingConfirmation = false + } + ) + } + } + .alert(isPresented: $transfereConfirmation) { + /// Checks if the user wants to proceed. If yes, execute request to the API + Alert( + title: Text("Er du sikker på at du vil fortsette?"), + message: Text(transfereMessage), + primaryButton: .default(Text("Ja")) { + transfereConfirmation = false + Task { + if !empty { + /// Starts request to transfere scaffoling + await transfereScaffoldingUnit(pickedFromID: Int(exactly: pickedFromID)!, pickedToID: Int(exactly: pickedToID)!, transfereAmount: Int(anumber)!) + } + } + // showingConfirmation = true + }, + secondaryButton: .cancel() { + transfereConfirmation = false + } + ) + } + } + + /// Establishes connection to the API and sendts a PUT request to the API with the scaffolding amount from which project to which project. + /// The code is partially taken and inspired from: + /// https://www.appsdeveloperblog.com/http-post-request-example-in-swift/ + /// - Parameters: + /// - pickedFromID: The ID of the project to transfere scaffolding from + /// - pickedToID: The ID of the project to transfere scaffolding to + /// - transfereAmount: The amount of scaffolding to transfere + func transfereScaffoldingUnit(pickedFromID: Int, pickedToID: Int, transfereAmount: Int) async { + let url = URL(string: "http://10.212.138.205:8080/stillastracking/v1/api/project/scaffolding") + guard let requestUrl = url else { fatalError() } + // Prepare URL Request Object + var request = URLRequest(url: requestUrl) + request.httpMethod = "PUT" + + /// Sets the body to be of type Scaff + let body : Scaff = Scaff(scaffold: [Move(type: "\(scaffolding.type.capitalizingFirstLetter())", quantity: transfereAmount)], toProjectID: pickedToID, fromProjectID: pickedFromID) + + /// Tries to encode body + guard let jsonData = try? JSONEncoder().encode(body) else { + print("Failed to encode order") + return + } + + /// Makes a jsonString of the encoded data + let jsonString = String(data: jsonData, encoding: .utf8)! + + /// Creates the request body with the jsonStrings data + request.httpBody = jsonString.data(using: String.Encoding.utf8); + + /// Perform HTTP Request + let task = URLSession.shared.dataTask(with: request) { (data, response, error) in + + /// Check for Error + if let error = error { + print("Error took place \(error)") + return + } + + /// Convert HTTP Response Data to a String + if let data = data, let dataString = String(data: data, encoding: .utf8) { + print("Response data string:\n \(dataString)") + } + + /// Checks the response's statuscode and displays the alert based on the code + if let response = response as? HTTPURLResponse { + if response.statusCode == 200 { + confirmationTitle = "Suksess!" + confirmationMessage = "Overføringen av \(body.scaffold[0].quantity)x \(body.scaffold[0].type) fra prosjekt \(body.fromProjectID) til prosjekt \(body.toProjectID) var en suksess!" + } else { + confirmationTitle = "Mislykket!" + confirmationMessage = "Overføringen av \(body.scaffold[0].quantity)x \(body.scaffold[0].type) fra prosjekt \(body.fromProjectID) til prosjekt \(body.toProjectID) feilet!" + } + showingConfirmation = true + } + } + task.resume() + } +} + +/* +struct TransfereScaffoldingView_Previews: PreviewProvider { + static var previews: some View { + TransfereScaffoldingView() + } +}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectListView.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectListView.swift new file mode 100644 index 0000000000000000000000000000000000000000..79aa8f0eb0972de501973687cb9944aa554c69fb --- /dev/null +++ b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/ProjectView/ProjectListView.swift @@ -0,0 +1,240 @@ +// +// ProjectView.swift +// stillasMobileApplication +// +// Created by Tormod Mork Muller on 28/03/2022. +// + +import SwiftUI +import UIKit + +/// Enum for the different filtertypes +enum FilterType { + case none, + period, + startBeforePeriod, + startAfterPeriod, + endBeforePeriod, + endAfterPeriod, + sizeBetween, + sizeEqualTo, + sizeLessThan, + sizeGreaterThan, + state, + county +} + +/// **ProjectRow** +/// The project lists information preview +struct ProjectRow: View { + /// The relevant project + var project: Project + + var body: some View { + HStack { + VStack(alignment: .leading) { + Text(project.projectName).font(.headline).bold().italic() + Text(project.period.startDate + " - " + project.period.endDate).font(.subheadline).foregroundColor(.gray) + } + Spacer() + Text(String(format: "%d", project.projectID)) + .foregroundColor(.gray) + } + } +} + +/// **ProjectListView** +/// The project list +struct ProjectListView: View { + /// Initializes an instance of ProjectData to call the loadData method + @ObservedObject var projectData: ProjectData = ProjectData() + + /// Data of searchQuery + @State var searchQuery = "" + + @State var hasFetchedData = false + + /// Initializes new Project instance to get project details + @State var projects = [Project]() + + /// Filter- or add project Modal Views active + @State private var showFilterModalView: Bool = false + @State private var showAddProjectModalView: Bool = false + + /// Initializes sort on size filter page + @State var sizeSortType: String = "Mellom" + + /// Initializes filter to be no filter + @State var filter: FilterType = .none + @State var filterArr: [String] = [] + @State var filterArrArea: [String] = [] + + // TODO: REMOVE? + /// Initialized values passed into filter + @State var projectStartDate = Date.distantPast + @State var projectEndDate = Date.distantFuture + @State var projectSize = 99999 + @State var minProjectSize = 100 + @State var maxProjectSize = 1000 + @State var projectState = "Active" + @State var projectCounty = "Innlandet" + + /// Data is loading + @State private var isLoading = true + + var body: some View { + VStack { + NavigationView { + ZStack { + /// Data is loading --> show ProgressView + if projectData.isLoading { + Spacer().frame(height:100) + ProgressView("Laster inn...") + .progressViewStyle(CircularProgressViewStyle(tint: .gray)) + .scaleEffect(x: 1.2, y: 1.2, anchor: .center) + } else { + /// Displays all projects + Form { + Section(header: Text("Alle Prosjekter")) { + /// List with all projects, filtered projects or searched for projects + List(searchResults, id: \.projectID) { project in + NavigationLink(destination: ProjectInfoView(projects: projects, project: project), label: { + ProjectRow(project: project) } + ) + } + .navigationTitle("Prosjekter") + } + } + .listStyle(.grouped) + .toolbar { + /// Button for adding filter + ToolbarItemGroup(placement: .navigationBarLeading) { + Button(action: { + print("Filter tapped!") + self.showFilterModalView.toggle() + }) { + Label("Filter", systemImage: "line.3.horizontal.decrease.circle") + } + } + + /// Button for adding project + ToolbarItemGroup(placement: .navigationBarTrailing) { + Button(action: { + print("Add project tapped!") + self.showAddProjectModalView.toggle() + }) { + Label("Legg til", systemImage: "plus.circle") + } + } + } + } + } + } + .task { + /// On first time opening --> Load in project data + await projectData.loadData { (projects) in + self.projects = projects + } + } + .sheet(isPresented: $showFilterModalView, + onDismiss: didDismiss) { + /// Modal View for filtering projects + FilterView(selStartDateBind: $projectStartDate, selEndDateBind: $projectEndDate, projectArea: $projectCounty, projectSize: $projectSize, projectStatus: $projectState, minProjectSize: $minProjectSize, maxProjectSize: $maxProjectSize, sizeSortType: $sizeSortType, filterArr: $filterArr, filterArrArea: $filterArrArea) + .onChange(of: filterArr) { filterVal in + if filterArr.contains("period") { + filter = .period + } + if filterArr.contains("area") { + filter = .county + } + if filterArr.contains("size") && sizeSortType == "Mellom" { + filter = .sizeBetween + } else if filterArr.contains("size") && sizeSortType == "Mindre enn" { + filter = .sizeLessThan + } else if filterArr.contains("size") && sizeSortType == "Større enn" { + filter = .sizeGreaterThan + } + if filterArr.contains("status") { + filter = .state + } + if filterArr.isEmpty { + filter = .none + } + } + } + .navigationViewStyle(.stack) + .searchable(text: $searchQuery, placement: .navigationBarDrawer(displayMode: .always)) + .refreshable { + /// Projectdata is fetches again from the API + await projectData.loadData { (projects) in + self.projects = projects + } + print("Refreshed") + } + } + .sheet(isPresented: $showAddProjectModalView, onDismiss: didDismiss){ + /// Modal View for adding project + /// Not implemented yet + Text("501") + .font(.system(size: 40).bold()) + Text("Not Implemented") + .font(.system(size: 30).bold()) + } + } + + /// Returns all the projects sorted and with the filter if applied + var searchResults: [Project] { + if searchQuery.isEmpty { + return filteredProjects.sorted { $0.projectName < $1.projectName } + } else { + return filteredProjects.filter { $0.projectName.lowercased().contains(searchQuery.lowercased()) }.sorted { $0.projectName < $1.projectName } + } + } + + + /// Modal View dismissed + func didDismiss() { + // Handle the dismissing action. + } + + /// The projects filtered based on the passed in filter + var filteredProjects: [Project] { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "dd/MM/yyyy" + + switch filter { + case .none: + return projects + case .period: + return projects.filter { dateFormatter.date(from: $0.period.startDate)! >= projectStartDate && dateFormatter.date(from: $0.period.endDate)! <= projectEndDate } + case .startBeforePeriod: + return projects.filter { dateFormatter.date(from: $0.period.startDate)! <= projectStartDate } + case .startAfterPeriod: + return projects.filter { dateFormatter.date(from: $0.period.startDate)! >= projectStartDate } + case .endBeforePeriod: + return projects.filter { dateFormatter.date(from: $0.period.endDate)! <= projectEndDate } + case .endAfterPeriod: + return projects.filter { dateFormatter.date(from: $0.period.endDate)! >= projectEndDate } + case .sizeBetween: + return projects.filter { $0.size >= Int(minProjectSize) && $0.size <= Int(maxProjectSize)} + case .sizeEqualTo: + return projects.filter { $0.size == Int(projectSize) } + case .sizeLessThan: + return projects.filter { $0.size < Int(minProjectSize) } + case .sizeGreaterThan: + return projects.filter { $0.size > Int(maxProjectSize) } + case .state: + return projects.filter { $0.state == projectState } + case .county: + return projects.filter { filterArrArea.contains($0.address.county)} + } + } +} + +/* +struct Project_Previews: PreviewProvider { + static var previews: some View { + ProjectListView() + } +} +*/ diff --git a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/TransfereScaffolding.swift b/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/TransfereScaffolding.swift deleted file mode 100644 index 360e1750bd3dde6802f857ecbc715c77f8152abb..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/ProjectView/TransfereScaffolding.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// TransfereScaffolding.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 05/04/2022. -// - -import SwiftUI - -struct TransfereScaffolding: View { - @State private var isShowingSheet = false - - var body: some View { - VStack { - Text("Transfere scaffolding") - .font(.title) - .padding(50) - Text(""" - Add transfere scaffolding functionality here. - """) - .padding(50) - Button("Dismiss", - action: { isShowingSheet.toggle() }) - } - } - - func didDismiss() { - // Handle the dismissing action. - } -} - -struct TransfereScaffolding_Previews: PreviewProvider { - static var previews: some View { - TransfereScaffolding() - } -} diff --git a/stillasMobileApplication/stillasMobileApplication/Views/Scaffolding/ScaffoldingList.swift b/stillasMobileApplication/stillasMobileApplication/Views/Scaffolding/ScaffoldingList.swift deleted file mode 100644 index 96f04645f86632aa7479b8e12fb7bb8adf950c38..0000000000000000000000000000000000000000 --- a/stillasMobileApplication/stillasMobileApplication/Views/Scaffolding/ScaffoldingList.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ScaffoldingList.swift -// stillasMobileApplication -// -// Created by Tormod Mork Muller on 28/03/2022. -// - -import SwiftUI -/* -struct ScaffoldingList: View { - - let scaffoldingUnits = [ - ScaffoldingUnit(name: "Spir", size: "2m", amount: 1400), - ScaffoldingUnit(name: "Spir", size: "3m", amount: 1500), - ScaffoldingUnit(name: "Lengdebjelke", size: "3m", amount: 480) - ] - @State var searchQuery = "" - - var body: some View { - VStack { - NavigationView { - List (scaffoldingUnits) { scaffolding in - NavigationLink(destination: DetailView(scaffolding: scaffolding)) { ScaffoldingUnitRow (scaffolding: scaffolding) } - } - .listStyle(PlainListStyle()) - .searchable(text: $searchQuery) - .navigationTitle("Scaffolding units") - } - } - } -} - -struct ScaffoldingList_Previews: PreviewProvider { - static var previews: some View { - ScaffoldingList() - } -}*/ diff --git a/stillasMobileApplication/stillasMobileApplication/stillasMobileApplicationApp.swift b/stillasMobileApplication/stillasMobileApplication/stillasMobileApplicationApp.swift index 53c7971a765aa7da7f51ae23424ed6e3048e83ee..93bc73635cab038fe37be8daae2a9c3e23e150da 100644 --- a/stillasMobileApplication/stillasMobileApplication/stillasMobileApplicationApp.swift +++ b/stillasMobileApplication/stillasMobileApplication/stillasMobileApplicationApp.swift @@ -6,12 +6,30 @@ // import SwiftUI +import Firebase + + +/// **AppDelegate** +/// Initializes the application with FirebaseApp +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + FirebaseApp.configure() + return true + } +} @main -struct stillasMobileApplicationApp: App { +/// **stillasMobileApplication** +/// Create an app by declaring a structure that conforms to the App protocol. +/// Assigns the AppDelegate. +struct stillasMobileApplication: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + var body: some Scene { WindowGroup { + let viewModel = AppViewModel() ContentView() + .environmentObject(viewModel) } } }