├── .deployment ├── .gitignore ├── CONTRIBUTING.md ├── Deploy-AzureResourceGroup.ps1 ├── LICENSE ├── README.md ├── azuredeploy.json ├── azuredeploy.parameters.json ├── client ├── FieldEngineerLite - Client.sln ├── FieldEngineerLite.Android │ ├── Assets │ │ └── AboutAssets.txt │ ├── FieldEngineerLite.Android.csproj │ ├── FieldEngineerLite.Android.sln │ ├── MainActivity.cs │ ├── Properties │ │ ├── AndroidManifest.xml │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── AboutResources.txt │ │ ├── Drawable │ │ │ └── Icon.png │ │ └── Resource.Designer.cs │ ├── app.config │ └── packages.config ├── FieldEngineerLite.iOS │ ├── AppDelegate.cs │ ├── FieldEngineerLite.iOS.csproj │ ├── FieldEngineerLite.iOS.sln │ ├── Info.plist │ ├── Main.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Resources │ │ ├── Default-568h@2x.png │ │ ├── Fabrikam-568h@2x.png │ │ └── Fabrikam-small@2x.png │ ├── app.config │ └── packages.config └── FieldEngineerLite │ ├── App.cs │ ├── Data │ └── EquipmentImages │ │ ├── Dish_1_Thumb.jpg │ │ ├── HDMI_1_Thumb.jpg │ │ ├── HDMI_2_Thumb.jpg │ │ ├── RCA_1_Thumb.jpg │ │ └── SetTop_1_Thumb.jpg │ ├── FieldEngineerLite.projitems │ ├── FieldEngineerLite.shproj │ ├── Helpers │ ├── JobStatusToColorConverter.cs │ └── LoggingHelpers.cs │ ├── Models │ ├── Customer.cs │ ├── Equipment.cs │ └── Job.cs │ ├── Services │ └── JobService.cs │ └── Views │ ├── JobCell.cs │ ├── JobDetailsPage.cs │ ├── JobGroupingHeaderCell.cs │ ├── JobHeaderView.cs │ ├── JobListPage.cs │ └── JobMasterDetailPage.cs ├── deploy.cmd └── server ├── FieldEngineer.sln └── FieldEngineer ├── App_Data └── Placeholder.txt ├── App_Start ├── BundleConfig.cs ├── FilterConfig.cs ├── RouteConfig.cs └── Startup.MobileApp.cs ├── Content ├── Site.css ├── bootstrap-theme.css ├── bootstrap-theme.min.css ├── bootstrap.css └── bootstrap.min.css ├── Controllers ├── AdminController.cs └── JobController.cs ├── DataObjects └── Job.cs ├── FieldEngineer.csproj ├── Global.asax ├── Global.asax.cs ├── Models └── JobDbContext.cs ├── Properties └── AssemblyInfo.cs ├── Scripts ├── bootstrap.js ├── bootstrap.min.js ├── jquery-1.10.2.intellisense.js ├── jquery-1.10.2.js ├── jquery-1.10.2.min.js ├── jquery-1.10.2.min.map ├── jquery.validate-vsdoc.js ├── jquery.validate.js ├── jquery.validate.min.js ├── jquery.validate.unobtrusive.js ├── jquery.validate.unobtrusive.min.js ├── knockout-2.2.1.debug.js ├── knockout-2.2.1.js └── modernizr-2.6.2.js ├── Startup.cs ├── Views ├── Admin │ ├── Create.cshtml │ ├── Delete.cshtml │ ├── Details.cshtml │ ├── Edit.cshtml │ └── Index.cshtml ├── Shared │ ├── Error.cshtml │ └── _Layout.cshtml ├── _ViewStart.cshtml └── web.config ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── _bin_deployableAssemblies ├── amd64 │ ├── Microsoft.VC90.CRT │ │ ├── Microsoft.VC90.CRT.manifest │ │ ├── README_ENU.txt │ │ └── msvcr90.dll │ ├── sqlceca40.dll │ ├── sqlcecompact40.dll │ ├── sqlceer40EN.dll │ ├── sqlceme40.dll │ ├── sqlceqp40.dll │ └── sqlcese40.dll └── x86 │ ├── Microsoft.VC90.CRT │ ├── Microsoft.VC90.CRT.manifest │ ├── README_ENU.txt │ └── msvcr90.dll │ ├── sqlceca40.dll │ ├── sqlcecompact40.dll │ ├── sqlceer40EN.dll │ ├── sqlceme40.dll │ ├── sqlceqp40.dll │ └── sqlcese40.dll ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf └── glyphicons-halflings-regular.woff └── packages.config /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = deploy.cmd 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | build/ 19 | bld/ 20 | [Bb]in/ 21 | [Oo]bj/ 22 | 23 | # Visual Studo 2015 cache/options directory 24 | .vs/ 25 | 26 | # MSTest test Results 27 | [Tt]est[Rr]esult*/ 28 | [Bb]uild[Ll]og.* 29 | 30 | # NUNIT 31 | *.VisualState.xml 32 | TestResult.xml 33 | 34 | # Build Results of an ATL Project 35 | [Dd]ebugPS/ 36 | [Rr]eleasePS/ 37 | dlldata.c 38 | 39 | *_i.c 40 | *_p.c 41 | *_i.h 42 | *.ilk 43 | *.meta 44 | *.obj 45 | *.pch 46 | *.pdb 47 | *.pgc 48 | *.pgd 49 | *.rsp 50 | *.sbr 51 | *.tlb 52 | *.tli 53 | *.tlh 54 | *.tmp 55 | *.tmp_proj 56 | *.log 57 | *.vspscc 58 | *.vssscc 59 | .builds 60 | *.pidb 61 | *.svclog 62 | *.scc 63 | 64 | # Chutzpah Test files 65 | _Chutzpah* 66 | 67 | # Visual C++ cache files 68 | ipch/ 69 | *.aps 70 | *.ncb 71 | *.opensdf 72 | *.sdf 73 | *.cachefile 74 | 75 | # Visual Studio profiler 76 | *.psess 77 | *.vsp 78 | *.vspx 79 | 80 | # TFS 2012 Local Workspace 81 | $tf/ 82 | 83 | # Guidance Automation Toolkit 84 | *.gpState 85 | 86 | # ReSharper is a .NET coding add-in 87 | _ReSharper*/ 88 | *.[Rr]e[Ss]harper 89 | *.DotSettings.user 90 | 91 | # JustCode is a .NET coding addin-in 92 | .JustCode 93 | 94 | # TeamCity is a build add-in 95 | _TeamCity* 96 | 97 | # DotCover is a Code Coverage Tool 98 | *.dotCover 99 | 100 | # NCrunch 101 | _NCrunch_* 102 | .*crunch*.local.xml 103 | 104 | # MightyMoose 105 | *.mm.* 106 | AutoTest.Net/ 107 | 108 | # Web workbench (sass) 109 | .sass-cache/ 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.[Pp]ublish.xml 129 | *.azurePubxml 130 | # TODO: Comment the next line if you want to checkin your web deploy settings 131 | # but database connection strings (with potential passwords) will be unencrypted 132 | *.pubxml 133 | *.publishproj 134 | 135 | # NuGet Packages 136 | *.nupkg 137 | # The packages folder can be ignored because of Package Restore 138 | **/packages/* 139 | # except build/, which is used as an MSBuild target. 140 | !**/packages/build/ 141 | # Uncomment if necessary however generally it will be regenerated when needed 142 | #!**/packages/repositories.config 143 | 144 | # Windows Azure Build Output 145 | csx/ 146 | *.build.csdef 147 | 148 | # Windows Store app package directory 149 | AppPackages/ 150 | 151 | # Others 152 | *.[Cc]ache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | bower_components/ 163 | 164 | # RIA/Silverlight projects 165 | Generated_Code/ 166 | 167 | # Backup & report files from converting an old project file 168 | # to a newer Visual Studio version. Backup files are not needed, 169 | # because we have git ;-) 170 | _UpgradeReport_Files/ 171 | Backup*/ 172 | UpgradeLog*.XML 173 | UpgradeLog*.htm 174 | 175 | # SQL Server files 176 | *.mdf 177 | *.ldf 178 | 179 | # Business Intelligence projects 180 | *.rdl.data 181 | *.bim.layout 182 | *.bim_*.settings 183 | 184 | # Microsoft Fakes 185 | FakesAssemblies/ 186 | 187 | # Node.js Tools for Visual Studio 188 | .ntvs_analysis.dat 189 | 190 | # Visual Studio 6 build log 191 | *.plg 192 | 193 | # Visual Studio 6 workspace options file 194 | *.opt 195 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Azure samples 2 | 3 | Thank you for your interest in contributing to Azure samples! 4 | 5 | ## Ways to contribute 6 | 7 | You can contribute to [Azure samples](https://azure.microsoft.com/documentation/samples/) in a few different ways: 8 | 9 | - Submit feedback on [this sample page](https://azure.microsoft.com/documentation/samples/app-service-mobile-dotnet-fieldengineer/) whether it was helpful or not. 10 | - Submit issues through [issue tracker](https://github.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/issues) on GitHub. We are actively monitoring the issues and improving our samples. 11 | - If you wish to make code changes to samples, or contribute something new, please follow the [GitHub Forks / Pull requests model](https://help.github.com/articles/fork-a-repo/): Fork the sample repo, make the change and propose it back by submitting a pull request. -------------------------------------------------------------------------------- /Deploy-AzureResourceGroup.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 3.0 2 | #Requires -Module AzureRM.Resources 3 | #Requires -Module Azure.Storage 4 | 5 | Param( 6 | [string] [Parameter(Mandatory=$true)] $ResourceGroupLocation, 7 | [string] [Parameter(Mandatory=$true)] $ResourceGroupName, 8 | [string] $TemplateFile = 'azuredeploy.json', 9 | [string] $TemplateParametersFile = 'azuredeploy.parameters.json' 10 | ) 11 | 12 | Import-Module Azure -ErrorAction SilentlyContinue 13 | 14 | try { 15 | [Microsoft.Azure.Common.Authentication.AzureSession]::ClientFactory.AddUserAgent("VSAzureTools-$UI$($host.name)".replace(" ","_"), "2.8") 16 | } catch { } 17 | 18 | Set-StrictMode -Version 3 19 | 20 | $OptionalParameters = New-Object -TypeName Hashtable 21 | $TemplateFile = [System.IO.Path]::Combine($PSScriptRoot, $TemplateFile) 22 | $TemplateParametersFile = [System.IO.Path]::Combine($PSScriptRoot, $TemplateParametersFile) 23 | 24 | # Create or update the resource group using the specified template file and template parameters file 25 | New-AzureRmResourceGroup -Name $ResourceGroupName -Location $ResourceGroupLocation -Verbose -Force -ErrorAction Stop 26 | 27 | New-AzureRmResourceGroupDeployment -Name ((Get-ChildItem $TemplateFile).BaseName + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')) ` 28 | -ResourceGroupName $ResourceGroupName ` 29 | -TemplateFile $TemplateFile ` 30 | -TemplateParameterFile $TemplateParametersFile ` 31 | @OptionalParameters ` 32 | -Force -Verbose 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | services: app-service\mobile, app-service\web, app-service 3 | platforms: dotnet, xamarin 4 | author: lindydonna 5 | --- 6 | 7 | # Azure App Service field engineer sample with web admin portal and offline-sync-enabled Xamarin.Forms client 8 | 9 | **Try out a simple version of this demo and other Xamarin demos on ["Try App Service"](https://aka.ms/trymobile).** 10 | 11 | This sample is for a mobile client app for field engineers at a cable company to more easily manage their appointments throughout the day. The app will sync the engineer's jobs for that day onto their device when there is an internet connection. When there isn't an internet connection, the Mobile Apps offline sync feature keeps the records available and edittable; when the engineer connects back to the internet, the local changes are synced and any new Jobs are pulled to their device. 12 | 13 | This sample shows off some great features of Azure Mobile Apps and App Service, including: 14 | - Offline Sync 15 | - Easy to use client SDK for Xamarin 16 | 17 | ### Overview 18 | 19 | The first step of the sample is deploying the environment and code. Checkout the **[Deploying](#deploying)** section below. Or, just click this button! 20 | 21 | [![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://azuredeploy.net/) 22 | 23 | Alternatively, you can deploy using the Azure Portal. [Click here to deploy](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Flindydonna%2Ffieldengineer%2Fmaster%2Fazuredeploy.json). Note: you can set the parameter **useSQLCE** to 1 or 0 to specify if you want to use SQLCE or a SQL Azure database. However, this template always creates a SQL Azure Database even if you set the SQLCE parameter to 1. 24 | 25 | Once you've refreshed the client to get all remote changes, you'll see all the jobs you have waiting to be fulfilled. At this point, you can disconnect your internet and make some changes. All your changes will be saved through a restart of the app. 26 | 27 | If you visit the admin page that comes with your site ({sitename}.azurewebsites.net/admin), you can see that none of the records have been updated. If you connect to the internet on your device again and refresh, your changes will sync to the server. You can see those changes reflected via the admin portal. 28 | 29 | ## Deploying 30 | 31 | Just click this button to deploy! 32 | 33 | [![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://azuredeploy.net/) 34 | 35 | ### Manual deployment - full version (with SQL Azure) 36 | 37 | The easiest way to deploy is to use the Continuous Integration feature of App Service. 38 | 39 | 1. Fork this repo in GitHub. 40 | 41 | 2. Create a new Mobile App from the portal. 42 | 43 | 3. In the **Mobile** -> **Data** section, create a new Data Connection. For more detailed instructions, see [Create a .NET backend using the Azure portal](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-dotnet-backend-how-to-use-server-sdk/#create-app). 44 | 45 | 4. In the portal, navigate to All Settings for the new app. 46 | 47 | 5. In the **Publishing** section, select **Continuous deployment**. 48 | 49 | 6. Configure source control to point to your fork of this repo. 50 | 51 | 6. Click the **Sync** button to do an initial deployment. 52 | 53 | ### Try App Service version (SQL CE) 54 | 55 | There is a project configuration that uses SQL CE as the server database and therefore does not require SQL Azure. 56 | 57 | To set up this version, after step #2 above, add an app setting with key `TRY_APP_SERVICE` and value `1`. To set an app setting, go to **All Settings** -> **Application Settings** -> **App Settings**. Then, continue with steps 3-6 above. 58 | 59 | ### Mobile client project 60 | 61 | Requirements: 62 | - Xamarin Studio running on Mac OS X 63 | - Azure Account (for a free trial, use [Try App Service](https://aka.ms/trymobile) and the instructions there) 64 | 65 | Steps: 66 | 67 | 1. Open up the Xamarin Project 68 | 69 | 2. Update the Mobile App Name, in `./Services/JobService.cs` 70 | 71 | 3. Select an iPad target and start debugging. 72 | 73 | ## Issues 74 | 75 | If you're having problems with the App, create an issue on [GitHub](https://github.com/azure/fieldengineer/issues). 76 | 77 | We welcome and encourage PRs from the community. Just checkout [Azure's CLA](https://cla.azure.com/) first. 78 | 79 | ## License 80 | 81 | See [LICENSE](https://github.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/blob/master/LICENSE) for full details. 82 | 83 | 84 | ### See also 85 | 86 | - The Xamarin CRM app utilizing Azure Mobile 87 | - [Try it now](aka.ms/trymobile) 88 | - [Source on GitHub](https://github.com/xamarin/app-crm/) 89 | - [Azure Mobile Apps on Azure.com](https://azure.microsoft.com/en-us/services/app-service/mobile/) 90 | - [Kirill Gavrylyuk giving this demo on Azure Fridays](https://channel9.msdn.com/Shows/Azure-Friday/Azure-App-Service-Mobile-Apps-with-Kirill-Gavrylyuk) 91 | -------------------------------------------------------------------------------- /azuredeploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appName": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The name of the mobile app that you wish to create." 9 | }, 10 | "minLength": 1 11 | }, 12 | "hostingPlanName": { 13 | "type": "string", 14 | "minLength": 1 15 | }, 16 | "skuName": { 17 | "type": "string", 18 | "defaultValue": "F1", 19 | "allowedValues": [ 20 | "F1", 21 | "D1", 22 | "B1", 23 | "B2", 24 | "B3", 25 | "S1", 26 | "S2", 27 | "S3", 28 | "P1", 29 | "P2", 30 | "P3", 31 | "P4" 32 | ], 33 | "metadata": { 34 | "description": "Describes plan's pricing tier and instance size. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/" 35 | } 36 | }, 37 | "repoUrl": { 38 | "type": "string" 39 | }, 40 | "administratorLogin": { 41 | "type": "string" 42 | }, 43 | "administratorLoginPassword": { 44 | "type": "securestring" 45 | }, 46 | "databaseName": { 47 | "type": "string" 48 | }, 49 | "useSQLCE": { 50 | "type": "string" 51 | } 52 | }, 53 | "variables": { 54 | "webSiteName":"[concat(parameters('appName'), uniqueString(resourceGroup().id))]", 55 | "sqlserverName": "[concat(toLower(parameters('appName')), uniqueString(resourceGroup().id))]" 56 | }, 57 | "resources": [ 58 | { 59 | "name": "[variables('sqlserverName')]", 60 | "type": "Microsoft.Sql/servers", 61 | "location": "[resourceGroup().location]", 62 | "tags": { 63 | "displayName": "SqlServer" 64 | }, 65 | "apiVersion": "2014-04-01-preview", 66 | "properties": { 67 | "administratorLogin": "[parameters('administratorLogin')]", 68 | "administratorLoginPassword": "[parameters('administratorLoginPassword')]" 69 | }, 70 | "resources": [ 71 | { 72 | "name": "[parameters('databaseName')]", 73 | "type": "databases", 74 | "location": "[resourceGroup().location]", 75 | "tags": { 76 | "displayName": "Database" 77 | }, 78 | "apiVersion": "2014-04-01-preview", 79 | "dependsOn": [ 80 | "[concat('Microsoft.Sql/servers/', variables('sqlserverName'))]" 81 | ], 82 | "properties": { 83 | "edition": "Basic", 84 | "collation": "SQL_Latin1_General_CP1_CI_AS", 85 | "maxSizeBytes": "1073741824", 86 | "requestedServiceObjectiveName": "Basic" 87 | } 88 | }, 89 | { 90 | "type": "firewallrules", 91 | "apiVersion": "2014-04-01-preview", 92 | "dependsOn": [ 93 | "[concat('Microsoft.Sql/servers/', variables('sqlserverName'))]" 94 | ], 95 | "location": "[resourceGroup().location]", 96 | "name": "AllowAllWindowsAzureIps", 97 | "properties": { 98 | "endIpAddress": "0.0.0.0", 99 | "startIpAddress": "0.0.0.0" 100 | } 101 | } 102 | ] 103 | }, 104 | { 105 | "apiVersion": "2015-08-01", 106 | "name": "[parameters('hostingPlanName')]", 107 | "type": "Microsoft.Web/serverfarms", 108 | "location": "[resourceGroup().location]", 109 | "tags": { 110 | "displayName": "HostingPlan" 111 | }, 112 | "sku": { 113 | "name": "[parameters('skuName')]", 114 | "capacity": "1" 115 | }, 116 | "properties": { 117 | "name": "[parameters('hostingPlanName')]" 118 | } 119 | }, 120 | { 121 | "apiVersion": "2015-08-01", 122 | "name": "[variables('webSiteName')]", 123 | "type": "Microsoft.Web/sites", 124 | "kind": "mobileApp", 125 | "location": "[resourceGroup().location]", 126 | "dependsOn": [ 127 | "[concat('Microsoft.Web/serverFarms/', parameters('hostingPlanName'))]" 128 | ], 129 | "tags": { 130 | "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "empty", 131 | "displayName": "Website" 132 | }, 133 | "properties": { 134 | "name": "[variables('webSiteName')]", 135 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]" 136 | }, 137 | "resources": [ 138 | { 139 | "apiVersion": "2015-08-01", 140 | "type": "config", 141 | "name": "connectionstrings", 142 | "dependsOn": [ 143 | "[concat('Microsoft.Web/Sites/', variables('webSiteName'))]", 144 | "[concat('Microsoft.Sql/servers/', variables('sqlserverName'))]" 145 | ], 146 | "properties": { 147 | "MS_TableConnectionString": { 148 | "value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', parameters('databaseName'), ';User Id=', parameters('administratorLogin'), '@', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]", 149 | "type": "SQLServer" 150 | } 151 | } 152 | }, 153 | { 154 | "apiVersion": "2015-08-01", 155 | "name": "appsettings", 156 | "type": "config", 157 | "dependsOn": [ 158 | "[concat('Microsoft.Web/Sites/', variables('webSiteName'))]" 159 | ], 160 | "properties": { 161 | "Command": "deploy.cmd", 162 | "TRY_APP_SERVICE": "[parameters('useSQLCE')]" 163 | } 164 | }, 165 | { 166 | "apiVersion": "2015-08-01", 167 | "name": "web", 168 | "type": "sourcecontrols", 169 | "dependsOn": [ 170 | "[concat('Microsoft.Web/Sites/', variables('webSiteName'))]" 171 | ], 172 | "properties": { 173 | "RepoUrl": "[parameters('repoUrl')]", 174 | "branch": "master", 175 | "IsManualIntegration": true 176 | } 177 | } 178 | ] 179 | } 180 | ] 181 | } 182 | -------------------------------------------------------------------------------- /azuredeploy.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "administratorLogin": { 6 | "value": "NewAdmin" 7 | }, 8 | "databaseName": { 9 | "value": "fieldengineer-sqldb" 10 | }, 11 | "hostingPlanName": { 12 | "value": "FieldEngineerLite-plan" 13 | }, 14 | "repoUrl": { 15 | "value": "https://github.com/lindydonna/fieldengineer" 16 | }, 17 | "administratorLoginPassword": { 18 | "value": "Not$ecurePassw0rd" 19 | }, 20 | "appName": { 21 | "value": "FieldEngineerLite" 22 | }, 23 | "useSQLCE": { 24 | "value": "1" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /client/FieldEngineerLite - Client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FieldEngineerLite.iOS", "FieldEngineerLite.iOS\FieldEngineerLite.iOS.csproj", "{59A5B44A-F4EC-4468-8695-BC5F2846F9CE}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FieldEngineerLite.Android", "FieldEngineerLite.Android\FieldEngineerLite.Android.csproj", "{BDFAB9D7-F1A2-401D-8260-9F593AD288C6}" 9 | EndProject 10 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "FieldEngineerLite", "FieldEngineerLite\FieldEngineerLite.shproj", "{F1003602-A8B5-4DC9-B601-99711543FF2E}" 11 | EndProject 12 | Global 13 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 14 | FieldEngineerLite\FieldEngineerLite.projitems*{f1003602-a8b5-4dc9-b601-99711543ff2e}*SharedItemsImports = 13 15 | FieldEngineerLite\FieldEngineerLite.projitems*{bdfab9d7-f1a2-401d-8260-9f593ad288c6}*SharedItemsImports = 4 16 | FieldEngineerLite\FieldEngineerLite.projitems*{59a5b44a-f4ec-4468-8695-bc5f2846f9ce}*SharedItemsImports = 4 17 | EndGlobalSection 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|iPhone Simulator = Debug|iPhone Simulator 20 | Debug|Mixed Platforms = Debug|Mixed Platforms 21 | Release|iPhone Simulator = Release|iPhone Simulator 22 | Release|Mixed Platforms = Release|Mixed Platforms 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhone Simulator.ActiveCfg = Debug|iPhoneSimulator 26 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhone Simulator.Build.0 = Debug|iPhoneSimulator 27 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|Mixed Platforms.ActiveCfg = Debug|iPhone 28 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|Mixed Platforms.Build.0 = Debug|iPhone 29 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhone Simulator.ActiveCfg = Release|iPhoneSimulator 30 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhone Simulator.Build.0 = Release|iPhoneSimulator 31 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|Mixed Platforms.ActiveCfg = Release|iPhoneSimulator 32 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|Mixed Platforms.Build.0 = Release|iPhoneSimulator 33 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|iPhone Simulator.ActiveCfg = Debug|Any CPU 34 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 35 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 36 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|Mixed Platforms.Deploy.0 = Debug|Any CPU 37 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|iPhone Simulator.ActiveCfg = Release|Any CPU 38 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 39 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|Mixed Platforms.Build.0 = Release|Any CPU 40 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|Mixed Platforms.Deploy.0 = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(MonoDevelopProperties) = preSolution 46 | Policies = $0 47 | $0.TextStylePolicy = $28 48 | $1.FileWidth = 120 49 | $1.RemoveTrailingWhitespace = False 50 | $1.NoTabsAfterNonTabs = True 51 | $1.inheritsSet = VisualStudio 52 | $1.inheritsScope = text/plain 53 | $1.scope = text/x-csharp 54 | $0.CSharpFormattingPolicy = $2 55 | $2.BeforeMethodDeclarationParentheses = False 56 | $2.BeforeMethodCallParentheses = False 57 | $2.BeforeConstructorDeclarationParentheses = False 58 | $2.BeforeDelegateDeclarationParentheses = False 59 | $2.AfterDelegateDeclarationParameterComma = True 60 | $2.NewParentheses = False 61 | $2.CatchParentheses = False 62 | $2.SwitchParentheses = False 63 | $2.SpacesBeforeBrackets = False 64 | $2.inheritsSet = Mono 65 | $2.inheritsScope = text/x-csharp 66 | $2.scope = text/x-csharp 67 | $0.StandardHeader = $3 68 | $3.Text = 69 | $3.IncludeInNewFiles = True 70 | $0.NameConventionPolicy = $4 71 | $4.Rules = $5 72 | $5.NamingRule = $25 73 | $6.Name = Namespaces 74 | $6.AffectedEntity = Namespace 75 | $6.VisibilityMask = VisibilityMask 76 | $6.NamingStyle = PascalCase 77 | $6.IncludeInstanceMembers = True 78 | $6.IncludeStaticEntities = True 79 | $7.Name = Types 80 | $7.AffectedEntity = Class, Struct, Enum, Delegate 81 | $7.VisibilityMask = Public 82 | $7.NamingStyle = PascalCase 83 | $7.IncludeInstanceMembers = True 84 | $7.IncludeStaticEntities = True 85 | $8.Name = Interfaces 86 | $8.RequiredPrefixes = $9 87 | $9.String = I 88 | $8.AffectedEntity = Interface 89 | $8.VisibilityMask = Public 90 | $8.NamingStyle = PascalCase 91 | $8.IncludeInstanceMembers = True 92 | $8.IncludeStaticEntities = True 93 | $10.Name = Attributes 94 | $10.RequiredSuffixes = $11 95 | $11.String = Attribute 96 | $10.AffectedEntity = CustomAttributes 97 | $10.VisibilityMask = Public 98 | $10.NamingStyle = PascalCase 99 | $10.IncludeInstanceMembers = True 100 | $10.IncludeStaticEntities = True 101 | $12.Name = Event Arguments 102 | $12.RequiredSuffixes = $13 103 | $13.String = EventArgs 104 | $12.AffectedEntity = CustomEventArgs 105 | $12.VisibilityMask = Public 106 | $12.NamingStyle = PascalCase 107 | $12.IncludeInstanceMembers = True 108 | $12.IncludeStaticEntities = True 109 | $14.Name = Exceptions 110 | $14.RequiredSuffixes = $15 111 | $15.String = Exception 112 | $14.AffectedEntity = CustomExceptions 113 | $14.VisibilityMask = VisibilityMask 114 | $14.NamingStyle = PascalCase 115 | $14.IncludeInstanceMembers = True 116 | $14.IncludeStaticEntities = True 117 | $16.Name = Methods 118 | $16.AffectedEntity = Methods 119 | $16.VisibilityMask = Protected, Public 120 | $16.NamingStyle = PascalCase 121 | $16.IncludeInstanceMembers = True 122 | $16.IncludeStaticEntities = True 123 | $17.Name = Static Readonly Fields 124 | $17.AffectedEntity = ReadonlyField 125 | $17.VisibilityMask = Protected, Public 126 | $17.NamingStyle = PascalCase 127 | $17.IncludeInstanceMembers = False 128 | $17.IncludeStaticEntities = True 129 | $18.Name = Fields 130 | $18.AffectedEntity = Field 131 | $18.VisibilityMask = Protected, Public 132 | $18.NamingStyle = PascalCase 133 | $18.IncludeInstanceMembers = True 134 | $18.IncludeStaticEntities = True 135 | $19.Name = ReadOnly Fields 136 | $19.AffectedEntity = ReadonlyField 137 | $19.VisibilityMask = Protected, Public 138 | $19.NamingStyle = PascalCase 139 | $19.IncludeInstanceMembers = True 140 | $19.IncludeStaticEntities = False 141 | $20.Name = Constant Fields 142 | $20.AffectedEntity = ConstantField 143 | $20.VisibilityMask = Protected, Public 144 | $20.NamingStyle = PascalCase 145 | $20.IncludeInstanceMembers = True 146 | $20.IncludeStaticEntities = True 147 | $21.Name = Properties 148 | $21.AffectedEntity = Property 149 | $21.VisibilityMask = Protected, Public 150 | $21.NamingStyle = PascalCase 151 | $21.IncludeInstanceMembers = True 152 | $21.IncludeStaticEntities = True 153 | $22.Name = Events 154 | $22.AffectedEntity = Event 155 | $22.VisibilityMask = Protected, Public 156 | $22.NamingStyle = PascalCase 157 | $22.IncludeInstanceMembers = True 158 | $22.IncludeStaticEntities = True 159 | $23.Name = Enum Members 160 | $23.AffectedEntity = EnumMember 161 | $23.VisibilityMask = VisibilityMask 162 | $23.NamingStyle = PascalCase 163 | $23.IncludeInstanceMembers = True 164 | $23.IncludeStaticEntities = True 165 | $24.Name = Parameters 166 | $24.AffectedEntity = Parameter 167 | $24.VisibilityMask = VisibilityMask 168 | $24.NamingStyle = CamelCase 169 | $24.IncludeInstanceMembers = True 170 | $24.IncludeStaticEntities = True 171 | $25.Name = Type Parameters 172 | $25.RequiredPrefixes = $26 173 | $26.String = T 174 | $25.AffectedEntity = TypeParameter 175 | $25.VisibilityMask = VisibilityMask 176 | $25.NamingStyle = PascalCase 177 | $25.IncludeInstanceMembers = True 178 | $25.IncludeStaticEntities = True 179 | $27.FileWidth = 120 180 | $27.TabsToSpaces = False 181 | $27.inheritsSet = VisualStudio 182 | $27.inheritsScope = text/plain 183 | $27.scope = text/plain 184 | $28.inheritsSet = null 185 | $28.scope = application/config+xml 186 | $0.XmlFormattingPolicy = $29 187 | $29.inheritsSet = null 188 | $29.scope = application/config+xml 189 | EndGlobalSection 190 | EndGlobal 191 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/Assets/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories) and given a Build Action of "AndroidAsset". 3 | 4 | These files will be deployed with you package and will be accessible using Android's 5 | AssetManager, like this: 6 | 7 | public class ReadAsset : Activity 8 | { 9 | protected override void OnCreate (Bundle bundle) 10 | { 11 | base.OnCreate (bundle); 12 | 13 | InputStream input = Assets.Open ("my_asset.txt"); 14 | } 15 | } 16 | 17 | Additionally, some Android functions will automatically load asset files: 18 | 19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); 20 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/FieldEngineerLite.Android.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6} 7 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | Library 9 | Properties 10 | FieldEngineerLite.Droid 11 | FieldEngineerLite.Android 12 | 512 13 | true 14 | Resources\Resource.Designer.cs 15 | Off 16 | Properties\AndroidManifest.xml 17 | v6.0 18 | armeabi,armeabi-v7a,x86 19 | 20 | 21 | 22 | 8.0.30703 23 | 2.0 24 | True 25 | 26 | 27 | true 28 | full 29 | false 30 | bin\Debug\ 31 | DEBUG;TRACE 32 | prompt 33 | 4 34 | None 35 | armeabi,armeabi-v7a,x86 36 | True 37 | 38 | True 39 | False 40 | False 41 | 42 | 43 | Xamarin 44 | False 45 | True 46 | 47 | 48 | pdbonly 49 | true 50 | bin\Release\ 51 | TRACE 52 | prompt 53 | 4 54 | False 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\MonoAndroid10\FormsViewGroup.dll 65 | True 66 | 67 | 68 | ..\packages\Microsoft.Azure.Mobile.Client.2.0.1\lib\monoandroid\Microsoft.WindowsAzure.Mobile.dll 69 | True 70 | 71 | 72 | ..\packages\Microsoft.Azure.Mobile.Client.2.0.1\lib\monoandroid\Microsoft.WindowsAzure.Mobile.Ext.dll 73 | True 74 | 75 | 76 | ..\packages\Microsoft.Azure.Mobile.Client.SQLiteStore.2.0.1\lib\portable-win+net45+wp8+wpa81+monotouch+monoandroid\Microsoft.WindowsAzure.Mobile.SQLiteStore.dll 77 | True 78 | 79 | 80 | 81 | 82 | ..\packages\Newtonsoft.Json.6.0.4\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll 83 | 84 | 85 | False 86 | ..\packages\SQLitePCL.3.8.7.2\lib\MonoAndroid\SQLitePCL.dll 87 | 88 | 89 | False 90 | ..\packages\SQLitePCL.3.8.7.2\lib\MonoAndroid\SQLitePCL.Ext.dll 91 | 92 | 93 | 94 | 95 | 96 | ..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Extensions.dll 97 | True 98 | 99 | 100 | ..\packages\Microsoft.Net.Http.2.2.29\lib\monoandroid\System.Net.Http.Primitives.dll 101 | True 102 | 103 | 104 | 105 | 106 | ..\packages\Xamarin.Android.Support.Design.23.0.1.3\lib\MonoAndroid403\Xamarin.Android.Support.Design.dll 107 | True 108 | 109 | 110 | ..\packages\Xamarin.Android.Support.v4.23.0.1.3\lib\MonoAndroid403\Xamarin.Android.Support.v4.dll 111 | True 112 | 113 | 114 | ..\packages\Xamarin.Android.Support.v7.AppCompat.23.0.1.3\lib\MonoAndroid403\Xamarin.Android.Support.v7.AppCompat.dll 115 | True 116 | 117 | 118 | ..\packages\Xamarin.Android.Support.v7.CardView.23.0.1.3\lib\MonoAndroid403\Xamarin.Android.Support.v7.CardView.dll 119 | True 120 | 121 | 122 | ..\packages\Xamarin.Android.Support.v7.MediaRouter.23.0.1.3\lib\MonoAndroid403\Xamarin.Android.Support.v7.MediaRouter.dll 123 | True 124 | 125 | 126 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\MonoAndroid10\Xamarin.Forms.Core.dll 127 | True 128 | 129 | 130 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\MonoAndroid10\Xamarin.Forms.Platform.dll 131 | True 132 | 133 | 134 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\MonoAndroid10\Xamarin.Forms.Platform.Android.dll 135 | True 136 | 137 | 138 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\MonoAndroid10\Xamarin.Forms.Xaml.dll 139 | True 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | Designer 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 164 | 165 | 166 | 167 | 168 | 169 | 176 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/FieldEngineerLite.Android.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FieldEngineerLite.Android", "FieldEngineerLite.Android.csproj", "{BDFAB9D7-F1A2-401D-8260-9F593AD288C6}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {BDFAB9D7-F1A2-401D-8260-9F593AD288C6}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | 5 | using Xamarin.Forms.Platform.Android; 6 | 7 | namespace FieldEngineerLite.Droid 8 | { 9 | [Activity(Label = "FieldEngineerLite", 10 | Icon = "@drawable/icon", 11 | MainLauncher = true, 12 | ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation, 13 | Theme = "@android:style/Theme.Holo.Light")] 14 | public class MainActivity : FormsApplicationActivity 15 | { 16 | protected override void OnCreate(Bundle bundle) 17 | { 18 | base.OnCreate(bundle); 19 | 20 | Xamarin.Forms.Forms.Init(this, bundle); 21 | Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init(); 22 | 23 | App.UIContext = this; 24 | 25 | var app = new App(); 26 | LoadApplication(app); 27 | } 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using Android.App; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("FieldEngineerLite.Android")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("FieldEngineerLite.Android")] 14 | [assembly: AssemblyCopyright("Copyright © 2014")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: ComVisible(false)] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.0.0")] 30 | [assembly: AssemblyFileVersion("1.0.0.0")] 31 | 32 | // Add some common permissions, these can be removed if not needed 33 | [assembly: UsesPermission(Android.Manifest.Permission.Internet)] 34 | [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] 35 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/Resources/AboutResources.txt: -------------------------------------------------------------------------------- 1 | Images, layout descriptions, binary blobs and string dictionaries can be included 2 | in your application as resource files. Various Android APIs are designed to 3 | operate on the resource IDs instead of dealing with images, strings or binary blobs 4 | directly. 5 | 6 | For example, a sample Android app that contains a user interface layout (main.xml), 7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) 8 | would keep its resources in the "Resources" directory of the application: 9 | 10 | Resources/ 11 | drawable-hdpi/ 12 | icon.png 13 | 14 | drawable-ldpi/ 15 | icon.png 16 | 17 | drawable-mdpi/ 18 | icon.png 19 | 20 | layout/ 21 | main.xml 22 | 23 | values/ 24 | strings.xml 25 | 26 | In order to get the build system to recognize Android resources, set the build action to 27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 28 | instead operate on resource IDs. When you compile an Android application that uses resources, 29 | the build system will package the resources for distribution and generate a class called 30 | "Resource" that contains the tokens for each one of the resources included. For example, 31 | for the above Resources layout, this is what the Resource class would expose: 32 | 33 | public class Resource { 34 | public class drawable { 35 | public const int icon = 0x123; 36 | } 37 | 38 | public class layout { 39 | public const int main = 0x456; 40 | } 41 | 42 | public class strings { 43 | public const int first_string = 0xabc; 44 | public const int second_string = 0xbcd; 45 | } 46 | } 47 | 48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main 49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first 50 | string in the dictionary file values/strings.xml. 51 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/Resources/Drawable/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite.Android/Resources/Drawable/Icon.png -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.Android/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | using Xamarin.Forms; 9 | 10 | namespace FieldEngineerLite.iOS 11 | { 12 | // The UIApplicationDelegate for the application. This class is responsible for launching the 13 | // User Interface of the application, as well as listening (and optionally responding) to 14 | // application events from iOS. 15 | [Register("AppDelegate")] 16 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 17 | { 18 | // class-level declarations 19 | //UIWindow window; 20 | 21 | // 22 | // This method is invoked when the application has loaded and is ready to run. In this 23 | // method you should instantiate the window, load the UI into it and then make the window 24 | // visible. 25 | // 26 | // You have 17 seconds to return from this method, or iOS will terminate your application. 27 | // 28 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) 29 | { 30 | Forms.Init(); 31 | Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init(); 32 | SQLitePCL.CurrentPlatform.Init(); 33 | 34 | var myapp = new App(); 35 | 36 | LoadApplication(myapp); 37 | 38 | var success = base.FinishedLaunching (app, options); 39 | App.UIContext = UIApplication.SharedApplication.KeyWindow.RootViewController; 40 | 41 | return success; 42 | 43 | } 44 | 45 | 46 | 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/FieldEngineerLite.iOS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | iPhoneSimulator 6 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE} 7 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | Exe 9 | FieldEngineerLite.iOS 10 | Resources 11 | 12 | 13 | Xamarin.iOS 14 | FieldEngineerLiteiOS 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\iPhoneSimulator\Debug 21 | DEBUG 22 | prompt 23 | 4 24 | false 25 | None 26 | True 27 | i386 28 | True 29 | 30 | 31 | iPhone Developer 32 | 1e0236de-4093-43d5-8571-7952d4437b6c 33 | FieldEngineerLiteiOS 34 | 35 | 36 | none 37 | true 38 | bin\iPhoneSimulator\Release 39 | prompt 40 | 4 41 | false 42 | None 43 | FieldEngineerLiteiOS 44 | 45 | 46 | false 47 | bin\iPhone\Debug 48 | 4 49 | iPhone Developer 50 | 51 | 52 | 53 | 54 | ARMv7 55 | 8.3 56 | FieldEngineerLiteiOS 57 | true 58 | 59 | 60 | none 61 | true 62 | bin\iPhone\Release 63 | prompt 64 | 4 65 | false 66 | iPhone Developer 67 | FieldEngineerLiteiOS 68 | ARMv7, ARM64 69 | 70 | 71 | none 72 | True 73 | bin\iPhone\Ad-Hoc 74 | prompt 75 | 4 76 | False 77 | iPhone Distribution 78 | True 79 | FieldEngineerLiteiOS 80 | ARMv7, ARM64 81 | 82 | 83 | none 84 | True 85 | bin\iPhone\AppStore 86 | prompt 87 | 4 88 | False 89 | iPhone Distribution 90 | FieldEngineerLiteiOS 91 | ARMv7, ARM64 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | ..\packages\Microsoft.Azure.Mobile.Client.2.0.1\lib\Xamarin.iOS10\Microsoft.WindowsAzure.Mobile.dll 104 | True 105 | 106 | 107 | ..\packages\Microsoft.Azure.Mobile.Client.2.0.1\lib\Xamarin.iOS10\Microsoft.WindowsAzure.Mobile.Ext.dll 108 | True 109 | 110 | 111 | ..\packages\Microsoft.Azure.Mobile.Client.SQLiteStore.2.0.1\lib\portable-win+net45+wp8+wpa81+monotouch+monoandroid\Microsoft.WindowsAzure.Mobile.SQLiteStore.dll 112 | True 113 | 114 | 115 | ..\packages\Newtonsoft.Json.6.0.4\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll 116 | 117 | 118 | 119 | 120 | 121 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll 122 | True 123 | 124 | 125 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll 126 | True 127 | 128 | 129 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll 130 | True 131 | 132 | 133 | ..\packages\Xamarin.Forms.2.0.0.6490\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll 134 | True 135 | 136 | 137 | 138 | ..\packages\SQLitePCL.3.8.7.2\lib\Xamarin.iOS10\SQLitePCL.dll 139 | 140 | 141 | ..\packages\SQLitePCL.3.8.7.2\lib\Xamarin.iOS10\SQLitePCL.Ext.dll 142 | 143 | 144 | 145 | ..\packages\Microsoft.Net.Http.2.2.28\lib\monotouch\System.Net.Http.Extensions.dll 146 | 147 | 148 | ..\packages\Microsoft.Net.Http.2.2.29\lib\Xamarin.iOS10\System.Net.Http.Primitives.dll 149 | 150 | 151 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\portable-wp8+netcore45+net45+wp81+wpa81\System.Net.Http.Formatting.dll 152 | 153 | 154 | 155 | 156 | 157 | Data\EquipmentImages\Dish_1_Thumb.jpg 158 | 159 | 160 | Data\EquipmentImages\HDMI_1_Thumb.jpg 161 | 162 | 163 | Data\EquipmentImages\HDMI_2_Thumb.jpg 164 | 165 | 166 | Data\EquipmentImages\RCA_1_Thumb.jpg 167 | 168 | 169 | Data\EquipmentImages\SetTop_1_Thumb.jpg 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/FieldEngineerLite.iOS.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FieldEngineerLite.iOS", "FieldEngineerLite.iOS.csproj", "{59A5B44A-F4EC-4468-8695-BC5F2846F9CE}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|iPhoneSimulator = Debug|iPhoneSimulator 9 | Release|iPhoneSimulator = Release|iPhoneSimulator 10 | Debug|iPhone = Debug|iPhone 11 | Release|iPhone = Release|iPhone 12 | Ad-Hoc|iPhone = Ad-Hoc|iPhone 13 | AppStore|iPhone = AppStore|iPhone 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone 17 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone 18 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.AppStore|iPhone.ActiveCfg = AppStore|iPhone 19 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.AppStore|iPhone.Build.0 = AppStore|iPhone 20 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhone.ActiveCfg = Debug|iPhone 21 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhone.Build.0 = Debug|iPhone 22 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator 23 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator 24 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhone.ActiveCfg = Release|iPhone 25 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhone.Build.0 = Release|iPhone 26 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator 27 | {59A5B44A-F4EC-4468-8695-BC5F2846F9CE}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 2 8 | 9 | UISupportedInterfaceOrientations 10 | 11 | UIInterfaceOrientationPortrait 12 | UIInterfaceOrientationLandscapeLeft 13 | UIInterfaceOrientationLandscapeRight 14 | 15 | MinimumOSVersion 16 | 8.3 17 | CFBundleIdentifier 18 | com.yourcompany.FieldEngineerLite 19 | CFBundleVersion 20 | 1.0 21 | CFBundleShortVersionString 22 | 23 | NSMainNibFile 24 | 25 | NSMainNibFile~ipad 26 | 27 | CFBundleIconFiles 28 | 29 | Default-568h@2x.png 30 | 31 | CFBundleDisplayName 32 | FieldEngineer 33 | 34 | 35 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using UIKit; 7 | 8 | namespace FieldEngineerLite.iOS 9 | { 10 | public class Application 11 | { 12 | // This is the main entry point of the application. 13 | static void Main(string[] args) 14 | { 15 | // if you want to use a different Application Delegate class from "AppDelegate" 16 | // you can specify it here. 17 | UIApplication.Main(args, null, "AppDelegate"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FieldEngineerLite.iOS")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FieldEngineerLite.iOS")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("72bdc44f-c588-44f3-b6df-9aace7daafdd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Resources/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite.iOS/Resources/Default-568h@2x.png -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Resources/Fabrikam-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite.iOS/Resources/Fabrikam-568h@2x.png -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/Resources/Fabrikam-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite.iOS/Resources/Fabrikam-small@2x.png -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/FieldEngineerLite.iOS/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/App.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Xamarin.Forms; 6 | using FieldEngineerLite.Views; 7 | using FieldEngineerLite; 8 | 9 | #if __IOS__ 10 | using UIContext = UIKit.UIViewController; 11 | #elif __ANDROID__ 12 | using UIContext = global::Android.Content.Context; 13 | #endif 14 | 15 | namespace FieldEngineerLite 16 | { 17 | public class App : Application 18 | { 19 | public static UIContext UIContext { get; set; } 20 | 21 | public App() 22 | { 23 | MainPage = new JobMasterDetailPage (); 24 | } 25 | } 26 | 27 | public static class AppStyle 28 | { 29 | public static NamedSize DefaultFontSize = Device.OnPlatform( 30 | iOS: NamedSize.Small, 31 | Android: NamedSize.Medium, 32 | WinPhone: NamedSize.Medium 33 | ); 34 | 35 | public static Color DefaultActionColor = Device.OnPlatform( 36 | iOS: Color.Blue, 37 | Android: Color.Accent, 38 | WinPhone: Color.Accent 39 | ); 40 | 41 | public static readonly Font DefaultFont = Device.OnPlatform( 42 | iOS: Font.OfSize("Avenir", DefaultFontSize), 43 | Android: Font.SystemFontOfSize(DefaultFontSize), 44 | WinPhone: Font.SystemFontOfSize(DefaultFontSize) 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Data/EquipmentImages/Dish_1_Thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite/Data/EquipmentImages/Dish_1_Thumb.jpg -------------------------------------------------------------------------------- /client/FieldEngineerLite/Data/EquipmentImages/HDMI_1_Thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite/Data/EquipmentImages/HDMI_1_Thumb.jpg -------------------------------------------------------------------------------- /client/FieldEngineerLite/Data/EquipmentImages/HDMI_2_Thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite/Data/EquipmentImages/HDMI_2_Thumb.jpg -------------------------------------------------------------------------------- /client/FieldEngineerLite/Data/EquipmentImages/RCA_1_Thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite/Data/EquipmentImages/RCA_1_Thumb.jpg -------------------------------------------------------------------------------- /client/FieldEngineerLite/Data/EquipmentImages/SetTop_1_Thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/client/FieldEngineerLite/Data/EquipmentImages/SetTop_1_Thumb.jpg -------------------------------------------------------------------------------- /client/FieldEngineerLite/FieldEngineerLite.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | f1003602-a8b5-4dc9-b601-99711543ff2e 7 | 8 | 9 | FieldEngineerLite 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/FieldEngineerLite.shproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | f1003602-a8b5-4dc9-b601-99711543ff2e 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Helpers/JobStatusToColorConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FieldEngineerLite.Models; 3 | using Xamarin.Forms; 4 | 5 | namespace FieldEngineerLite.Helpers 6 | { 7 | public class JobStatusToColorConverter : IValueConverter 8 | { 9 | private bool useLightTheme; 10 | 11 | public JobStatusToColorConverter(bool useLightTheme = false) 12 | { 13 | this.useLightTheme = useLightTheme; 14 | } 15 | 16 | private Color GetColorFromStatus(string status) 17 | { 18 | switch (status) 19 | { 20 | case Job.InProgressStatus: 21 | return Color.Green; 22 | case Job.PendingStatus: 23 | return Color.FromRgb(210, 90, 0); 24 | case Job.CompleteStatus: 25 | return Color.Blue; 26 | default: 27 | throw new ArgumentException(string.Format("Unknown status: '{0}'", status), "status"); 28 | } 29 | } 30 | 31 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 32 | { 33 | var status = (string)value; 34 | var color = GetColorFromStatus(status); 35 | if (useLightTheme) 36 | { 37 | var luminosityModifier = Device.OnPlatform( 38 | iOS: 0.95, 39 | Android: 0.1, 40 | WinPhone: 0.1 41 | ); 42 | 43 | color = color.WithLuminosity(luminosityModifier); 44 | } 45 | 46 | return color; 47 | } 48 | 49 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 50 | { 51 | throw new NotImplementedException(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Helpers/LoggingHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Microsoft.WindowsAzure.MobileServices.SQLiteStore; 6 | 7 | namespace FieldEngineerLite.Helpers 8 | { 9 | public class MobileServiceSQLiteStoreWithLogging : MobileServiceSQLiteStore 10 | { 11 | private bool logResults; 12 | private bool logParameters; 13 | 14 | public MobileServiceSQLiteStoreWithLogging(string fileName, bool logResults = false, bool logParameters = false) 15 | : base(fileName) 16 | { 17 | this.logResults = logResults; 18 | this.logParameters = logParameters; 19 | } 20 | 21 | protected override IList ExecuteQuery(string tableName, string sql, IDictionary parameters) 22 | { 23 | Debug.WriteLine (sql); 24 | 25 | if(logParameters) 26 | PrintDictionary (parameters); 27 | 28 | var result = base.ExecuteQuery(tableName, sql, parameters); 29 | 30 | if (logResults && result != null) 31 | { 32 | foreach (var token in result) 33 | Debug.WriteLine (token); 34 | } 35 | 36 | return result; 37 | } 38 | 39 | protected override void ExecuteNonQuery(string sql, IDictionary parameters) 40 | { 41 | Debug.WriteLine (sql); 42 | 43 | if(logParameters) 44 | PrintDictionary (parameters); 45 | 46 | base.ExecuteNonQuery(sql, parameters); 47 | } 48 | 49 | private void PrintDictionary(IDictionary dictionary) 50 | { 51 | if (dictionary == null) 52 | return; 53 | 54 | foreach (var pair in dictionary) 55 | Debug.WriteLine ("{0}:{1}", pair.Key, pair.Value); 56 | } 57 | } 58 | 59 | public class LoggingHandler : DelegatingHandler 60 | { 61 | private bool logRequestResponseBody; 62 | 63 | public LoggingHandler(bool logRequestResponseBody = false) 64 | { 65 | this.logRequestResponseBody = logRequestResponseBody; 66 | } 67 | 68 | protected override async Task SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) 69 | { 70 | Debug.WriteLine("Request: {0} {1}", request.Method, request.RequestUri.ToString()); 71 | 72 | if (logRequestResponseBody && request.Content != null) 73 | { 74 | var requestContent = await request.Content.ReadAsStringAsync (); 75 | Debug.WriteLine (requestContent); 76 | } 77 | 78 | var response = await base.SendAsync(request, cancellationToken); 79 | 80 | Debug.WriteLine ("Response: {0}", response.StatusCode); 81 | 82 | if (logRequestResponseBody) 83 | { 84 | var responseContent = await response.Content.ReadAsStringAsync (); 85 | Debug.WriteLine (responseContent); 86 | } 87 | 88 | return response; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FieldEngineerLite.Models 4 | { 5 | public class Customer 6 | { 7 | public string Id { get; set; } 8 | public string FullName { get; set; } 9 | public string HouseNumberOrName { get; set; } 10 | public string Street { get; set; } 11 | public string Town { get; set; } 12 | public string County { get; set; } 13 | public string Postcode { get; set; } 14 | public string PrimaryContactNumber { get; set; } 15 | public string AdditionalContactNumber { get; set; } 16 | public double Latitude { get; set; } 17 | public double Longitude { get; set; } 18 | public string WorkPerformed {get; set;} 19 | 20 | public string Address 21 | { 22 | get { return string.Format ("{0} {1} {2} {3} {4}", HouseNumberOrName, Street, Town, County, Postcode); } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Models/Equipment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FieldEngineerLite.Models 4 | { 5 | public class WorkItem 6 | { 7 | public string Name { get; set; } 8 | 9 | public bool Completed {get; set;} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Models/Job.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using Microsoft.WindowsAzure.MobileServices; 5 | using Newtonsoft.Json; 6 | 7 | namespace FieldEngineerLite.Models 8 | { 9 | public class Job : INotifyPropertyChanged 10 | { 11 | public Job() 12 | { 13 | Items = new List 14 | { 15 | new WorkItem {Name = "Replace cable box", Completed = false}, 16 | new WorkItem {Name = "Repair cable outlet", Completed = false}, 17 | new WorkItem {Name = "Fix cable wiring", Completed = false} 18 | }; 19 | } 20 | 21 | public const string CompleteStatus = "Completed"; 22 | public const string InProgressStatus = "In Progress"; 23 | public const string PendingStatus = "Not Started"; 24 | 25 | public string Id { get; set; } 26 | public string AgentId { get; set; } 27 | public string JobNumber { get; set; } 28 | public string StartTime { get; set; } 29 | public string EndTime { get; set; } 30 | public string Title { get; set; } 31 | public string CustomerName { get; set; } 32 | public string CustomerAddress { get; set; } 33 | public string CustomerPhoneNumber { get; set; } 34 | public string WorkPerformed { get; set; } 35 | 36 | private string status; 37 | 38 | public event PropertyChangedEventHandler PropertyChanged; 39 | 40 | private void OnPropertyChanged(string propertyName) => 41 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 42 | 43 | public string Status 44 | { 45 | get { return status; } 46 | set 47 | { 48 | status = value; 49 | OnPropertyChanged(nameof(Status)); 50 | } 51 | } 52 | 53 | [Version] 54 | public string Version { get; set; } 55 | 56 | [JsonIgnore] 57 | public List Items { get; set; } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Services/JobService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Microsoft.WindowsAzure.MobileServices; 5 | using Microsoft.WindowsAzure.MobileServices.SQLiteStore; 6 | using Microsoft.WindowsAzure.MobileServices.Sync; 7 | using FieldEngineerLite.Helpers; 8 | using FieldEngineerLite.Models; 9 | using Microsoft.WindowsAzure.MobileServices.Eventing; 10 | using System.Diagnostics; 11 | 12 | namespace FieldEngineerLite 13 | { 14 | public class JobService 15 | { 16 | public bool Online = false; 17 | 18 | public IMobileServiceClient MobileService = null; 19 | private IMobileServiceSyncTable jobTable; 20 | 21 | // Placeholder string for Try App Service is ZUMOAPPURL 22 | // To use with your own app, use URL in the form https://your-site-name.azurewebsites.net/ 23 | private const string MobileUrl = "ZUMOAPPURL"; 24 | 25 | public async Task InitializeAsync() 26 | { 27 | this.MobileService = 28 | new MobileServiceClient(MobileUrl, new LoggingHandler()); 29 | 30 | var store = new MobileServiceSQLiteStore("local.db"); 31 | store.DefineTable(); 32 | 33 | await MobileService.SyncContext.InitializeAsync(store, StoreTrackingOptions.NotifyLocalAndServerOperations); 34 | jobTable = MobileService.GetSyncTable(); 35 | 36 | // This sample doesn't do any authentication. To add it, see 37 | // https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started-users/ 38 | } 39 | 40 | public async Task> ReadJobs(string search) 41 | { 42 | return await jobTable.ToEnumerableAsync(); 43 | } 44 | 45 | public async Task UpdateJobAsync(Job job) 46 | { 47 | job.Status = Job.CompleteStatus; 48 | 49 | await jobTable.UpdateAsync(job); 50 | 51 | // trigger an event so that the job list is refreshed 52 | await MobileService.EventManager.PublishAsync(new MobileServiceEvent("JobChanged")); 53 | } 54 | 55 | public async Task SyncAsync() 56 | { 57 | try 58 | { 59 | await this.MobileService.SyncContext.PushAsync(); 60 | await jobTable.PullAsync(null, jobTable.CreateQuery()); 61 | } 62 | catch (Exception e) 63 | { 64 | Debug.WriteLine(e); 65 | } 66 | } 67 | 68 | public async Task CompleteJobAsync(Job job) 69 | { 70 | await UpdateJobAsync(job); 71 | 72 | if (Online) 73 | await this.SyncAsync(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobCell.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms; 2 | using FieldEngineerLite.Helpers; 3 | using FieldEngineerLite.Models; 4 | 5 | namespace FieldEngineerLite.Views 6 | { 7 | public class JobCell : ViewCell 8 | { 9 | public JobCell() 10 | { 11 | var jobHeader = new JobHeaderView(leftPadding: 5); 12 | 13 | var title = new Label { FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)) }; 14 | title.SetBinding(Label.TextProperty, job => job.Title); 15 | 16 | var customer = new Label { FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)) }; 17 | customer.SetBinding(Label.TextProperty, job => job.CustomerName); 18 | 19 | 20 | var jobDetails = new StackLayout 21 | { 22 | Orientation = StackOrientation.Vertical, 23 | Padding = 5, 24 | Children = 25 | { 26 | new StackLayout 27 | { 28 | Orientation = StackOrientation.Horizontal, 29 | Children = { 30 | new Label { 31 | Text = "Customer:", 32 | FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), 33 | FontAttributes = FontAttributes.Bold 34 | }, 35 | customer 36 | } 37 | }, 38 | new Label { 39 | Text = "Description:", 40 | FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), 41 | FontAttributes = FontAttributes.Bold 42 | }, 43 | title 44 | } 45 | }; 46 | 47 | var rootLayout = new StackLayout() 48 | { 49 | Orientation = StackOrientation.Vertical, 50 | Spacing = 0, 51 | Children = 52 | { 53 | jobHeader, 54 | jobDetails 55 | } 56 | }; 57 | 58 | this.Height = 120; 59 | this.View = rootLayout; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobDetailsPage.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Xamarin.Forms; 3 | using FieldEngineerLite.Helpers; 4 | using FieldEngineerLite.Models; 5 | using System.Linq; 6 | 7 | namespace FieldEngineerLite.Views 8 | { 9 | public class JobDetailsPage : ContentPage 10 | { 11 | private JobService jobService; 12 | 13 | public JobDetailsPage(JobService service) 14 | { 15 | this.jobService = service; 16 | 17 | this.Title = "Appointment Details"; 18 | 19 | TableSection mainSection = new TableSection("Customer Details"); 20 | 21 | mainSection.Add(new DataElementCell("CustomerName", "Customer")); 22 | mainSection.Add(new DataElementCell("Title", "Customer Notes")); 23 | mainSection.Add(new DataElementCell("CustomerAddress", "Address") { Height = 60 }); 24 | mainSection.Add(new DataElementCell("CustomerPhoneNumber", "Telephone")); 25 | 26 | var statusCell = new DataElementCell("Status"); 27 | statusCell.ValueLabel.SetBinding(Label.TextColorProperty, job => job.Status, converter: new JobStatusToColorConverter()); 28 | mainSection.Add(statusCell); 29 | 30 | var workSection = new TableSection("Work Performed"); 31 | var workRowTemplate = new DataTemplate(typeof(SwitchCell)); 32 | workRowTemplate.SetBinding(SwitchCell.TextProperty, "Name"); 33 | workRowTemplate.SetBinding(SwitchCell.OnProperty, "Completed"); 34 | //workRowTemplate.SetValue(TextCell.TextColorProperty, Color.White); 35 | 36 | // I don't have images working on Android yet 37 | //if (Device.OS == TargetPlatform.iOS) 38 | // equipmentRowTemplate.SetBinding (ImageCell.ImageSourceProperty, "ThumbImage"); 39 | 40 | var workListView = new ListView { 41 | RowHeight = 50, 42 | ItemTemplate = workRowTemplate 43 | }; 44 | workListView.SetBinding(ListView.ItemsSourceProperty, job => job.Items); 45 | 46 | var workCell = new ViewCell { View = workListView }; 47 | 48 | workSection.Add(workCell); 49 | 50 | var actionsSection = new TableSection("Actions"); 51 | 52 | TextCell completeJob = new TextCell { 53 | Text = "Mark Completed", 54 | TextColor = AppStyle.DefaultActionColor 55 | }; 56 | 57 | completeJob.Tapped += async delegate { 58 | await this.CompleteJobAsync(); 59 | }; 60 | 61 | actionsSection.Add(completeJob); 62 | 63 | var table = new TableView 64 | { 65 | Intent = TableIntent.Form, 66 | VerticalOptions = LayoutOptions.FillAndExpand, 67 | HasUnevenRows = true, 68 | Root = new TableRoot("Root") 69 | { 70 | mainSection, workSection, actionsSection, 71 | } 72 | }; 73 | 74 | this.Content = new ScrollView { 75 | Orientation = ScrollOrientation.Vertical, 76 | Content = new StackLayout 77 | { 78 | Orientation = StackOrientation.Vertical, 79 | Children = { new JobHeaderView(leftPadding: 10, colorBackground: true), table } 80 | } 81 | }; 82 | 83 | this.BindingContextChanged += delegate 84 | { 85 | if (SelectedJob != null && SelectedJob.Items != null) 86 | workCell.Height = SelectedJob.Items.Count * workListView.RowHeight; 87 | }; 88 | } 89 | 90 | private async Task CompleteJobAsync() 91 | { 92 | var job = this.SelectedJob; 93 | 94 | job.WorkPerformed = string.Join(";", job.Items.Where(i => i.Completed).Select(i => i.Name)); 95 | 96 | await jobService.CompleteJobAsync(job); 97 | 98 | // Force a refresh 99 | this.BindingContext = null; 100 | this.BindingContext = job; 101 | } 102 | 103 | private Job SelectedJob 104 | { 105 | get { return this.BindingContext as Job; } 106 | } 107 | 108 | private class DataElementCell : ViewCell 109 | { 110 | public Label DescriptionLabel { get; set; } 111 | public Label ValueLabel { get; set; } 112 | 113 | public DataElementCell(string property, string propertyDescription = null) 114 | { 115 | DescriptionLabel = new Label { 116 | Text = propertyDescription ?? property, 117 | FontAttributes = FontAttributes.Bold, 118 | VerticalOptions = LayoutOptions.CenterAndExpand, 119 | }; 120 | 121 | ValueLabel = new Label { 122 | HorizontalOptions = LayoutOptions.FillAndExpand, 123 | VerticalOptions = LayoutOptions.CenterAndExpand, 124 | HorizontalTextAlignment = TextAlignment.End, 125 | }; 126 | ValueLabel.SetBinding(Label.TextProperty, property); 127 | 128 | this.View = new StackLayout 129 | { 130 | Orientation = StackOrientation.Horizontal, 131 | Padding = new Thickness(15, 10), 132 | Children = { DescriptionLabel, ValueLabel } 133 | }; 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobGroupingHeaderCell.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms; 2 | using FieldEngineerLite.Helpers; 3 | 4 | namespace FieldEngineerLite.Views 5 | { 6 | public class JobGroupingHeaderCell : ViewCell 7 | { 8 | public JobGroupingHeaderCell() 9 | { 10 | var title = new Label 11 | { 12 | FontAttributes = FontAttributes.Bold, 13 | TextColor = Color.White, 14 | VerticalOptions = LayoutOptions.Center 15 | }; 16 | 17 | title.SetBinding(Label.TextProperty, new Binding("Key", stringFormat: "Status: {0}")); 18 | 19 | var layout = new StackLayout 20 | { 21 | Padding = new Thickness(5, 5, 5, 0), 22 | Orientation = StackOrientation.Horizontal, 23 | Children = { title } 24 | }; 25 | 26 | layout.SetBinding(StackLayout.BackgroundColorProperty, "Key", converter: new JobStatusToColorConverter()); 27 | 28 | this.View = layout; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobHeaderView.cs: -------------------------------------------------------------------------------- 1 | using Xamarin.Forms; 2 | using FieldEngineerLite.Helpers; 3 | using FieldEngineerLite.Models; 4 | 5 | namespace FieldEngineerLite 6 | { 7 | public class JobHeaderView : ContentView 8 | { 9 | public JobHeaderView(double leftPadding, bool colorBackground = false) 10 | { 11 | var eta = new Label(); 12 | eta.VerticalOptions = LayoutOptions.FillAndExpand; 13 | eta.HorizontalOptions = LayoutOptions.FillAndExpand; 14 | eta.VerticalTextAlignment = TextAlignment.Start; 15 | eta.SetBinding(Label.TextProperty, job => job.StartTime, stringFormat: "Start time: {0}"); 16 | 17 | var rootLayout = new StackLayout { 18 | Orientation = StackOrientation.Horizontal, 19 | Padding = new Thickness(leftPadding, 5, 0, 5), 20 | Children = { eta } 21 | }; 22 | 23 | if (colorBackground) { 24 | eta.TextColor = Color.White; 25 | rootLayout.SetBinding(StackLayout.BackgroundColorProperty, job => job.Status, converter: new JobStatusToColorConverter()); 26 | } 27 | 28 | this.Content = rootLayout; 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobListPage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Xamarin.Forms; 5 | using FieldEngineerLite.Models; 6 | using Microsoft.WindowsAzure.MobileServices.Eventing; 7 | using System.ComponentModel; 8 | using Microsoft.WindowsAzure.MobileServices.Sync; 9 | 10 | namespace FieldEngineerLite.Views 11 | { 12 | public class JobListPage : ContentPage 13 | { 14 | private const bool DEFAULT_ONLINE_STATE = false; 15 | public ListView JobList; 16 | private JobService jobService; 17 | private long pendingChanges; 18 | 19 | public JobListPage(JobService service) 20 | { 21 | this.jobService = service; 22 | 23 | JobList = new ListView 24 | { 25 | HasUnevenRows = true, 26 | IsGroupingEnabled = true, 27 | GroupDisplayBinding = new Binding("Key"), 28 | GroupHeaderTemplate = new DataTemplate(typeof(JobGroupingHeaderCell)), 29 | ItemTemplate = new DataTemplate(typeof(JobCell)) 30 | }; 31 | 32 | var onlineLabel = new Label { Text = "Online", VerticalTextAlignment = TextAlignment.Center }; 33 | var onlineSwitch = new Switch { IsToggled = DEFAULT_ONLINE_STATE, VerticalOptions = LayoutOptions.Center }; 34 | 35 | jobService.Online = onlineSwitch.IsToggled; 36 | 37 | onlineSwitch.Toggled += async (sender, e) => 38 | { 39 | jobService.Online = onlineSwitch.IsToggled; 40 | 41 | if (onlineSwitch.IsToggled) 42 | { 43 | await jobService.SyncAsync(); 44 | await this.RefreshAsync(); 45 | } 46 | }; 47 | 48 | var syncButton = new Button 49 | { 50 | HorizontalOptions = LayoutOptions.CenterAndExpand, 51 | VerticalOptions = LayoutOptions.CenterAndExpand, 52 | Text = "Refresh", 53 | FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Button)), 54 | WidthRequest = 120, 55 | }; 56 | 57 | syncButton.Clicked += async (object sender, EventArgs e) => 58 | { 59 | try 60 | { 61 | syncButton.Text = "Refreshing..."; 62 | await jobService.SyncAsync(); 63 | await this.RefreshAsync(); 64 | } 65 | finally 66 | { 67 | syncButton.Text = "Refresh"; 68 | } 69 | }; 70 | 71 | this.Title = "Appointments"; 72 | 73 | var statusBar = new Label { BackgroundColor = Color.Gray, TextColor = Color.White }; 74 | statusBar.SetBinding(Label.TextProperty, "PendingChanges", stringFormat: "Pending changes: {0}"); 75 | statusBar.BindingContext = this; 76 | 77 | this.Content = new StackLayout { 78 | Orientation = StackOrientation.Vertical, 79 | Padding = new Thickness { Top = 15 }, 80 | Children = { 81 | new StackLayout { 82 | 83 | Orientation = StackOrientation.Horizontal, 84 | HorizontalOptions = LayoutOptions.StartAndExpand, 85 | Children = { 86 | syncButton, new Label { Text = " "}, onlineLabel, onlineSwitch 87 | } 88 | }, 89 | JobList, 90 | new ContentView { 91 | Content = statusBar, Padding = 5, BackgroundColor = Color.Gray, 92 | } 93 | } 94 | }; 95 | } 96 | 97 | public long PendingChanges 98 | { 99 | get 100 | { 101 | return pendingChanges; 102 | } 103 | 104 | set 105 | { 106 | pendingChanges = value; 107 | OnPropertyChanged(); 108 | } 109 | } 110 | 111 | private void StatusObserver(MobileServiceEvent obj) 112 | { 113 | // Refresh the UI if a job was edited on the detail page 114 | if (obj.Name == "JobChanged") { 115 | Device.BeginInvokeOnMainThread(async () => { 116 | await RefreshAsync(); 117 | }); 118 | } 119 | } 120 | 121 | protected async override void OnAppearing() 122 | { 123 | base.OnAppearing(); 124 | await this.RefreshAsync(); 125 | 126 | jobService.MobileService.EventManager.Subscribe(StatusObserver); 127 | jobService.MobileService.EventManager.Subscribe(StoreOperationEventHandler); 128 | } 129 | 130 | private async void StoreOperationEventHandler(StoreOperationCompletedEvent obj) 131 | { 132 | await Task.Delay(500); 133 | PendingChanges = jobService.MobileService.SyncContext.PendingOperations; 134 | } 135 | 136 | public async Task RefreshAsync() 137 | { 138 | var groups = from job in await jobService.ReadJobs("") 139 | group job by job.Status into jobGroup 140 | select jobGroup; 141 | 142 | JobList.ItemsSource = groups.OrderBy(group => GetJobSortOrder(group.Key)); 143 | } 144 | 145 | private int GetJobSortOrder(string status) 146 | { 147 | switch (status) 148 | { 149 | case Job.InProgressStatus: return 0; 150 | case Job.PendingStatus: return 1; 151 | case Job.CompleteStatus: return 2; 152 | default: return -1; 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /client/FieldEngineerLite/Views/JobMasterDetailPage.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Xamarin.Forms; 3 | using FieldEngineerLite.Models; 4 | using FieldEngineerLite.Views; 5 | 6 | namespace FieldEngineerLite 7 | { 8 | public class MyNavigationPage : NavigationPage 9 | { 10 | public MyNavigationPage(Page root) 11 | : base(root) 12 | { 13 | NavigationPage.SetHasNavigationBar(this, false); 14 | } 15 | } 16 | 17 | public class JobMasterDetailPage : MasterDetailPage 18 | { 19 | private JobService jobService = new JobService(); 20 | 21 | public JobMasterDetailPage() 22 | { 23 | JobListPage listPage = new JobListPage(jobService); 24 | listPage.JobList.ItemSelected += (sender, e) => 25 | { 26 | var selectedJob = e.SelectedItem as Job; 27 | if (selectedJob != null) 28 | { 29 | NavigateTo(e.SelectedItem as Job); 30 | } 31 | }; 32 | 33 | var listNavigationPage = new MyNavigationPage(listPage); 34 | listNavigationPage.Title = "Appointments"; 35 | Master = listNavigationPage; 36 | JobDetailsPage details = new JobDetailsPage(jobService); 37 | 38 | details.Content.IsVisible = false; 39 | Detail = new MyNavigationPage(details); 40 | } 41 | 42 | protected async override void OnAppearing() 43 | { 44 | base.OnAppearing(); 45 | await jobService.InitializeAsync(); 46 | 47 | var jobs = await jobService.ReadJobs(""); 48 | if (jobs.Count() > 0) 49 | { 50 | Job job = jobs.First(); 51 | NavigateTo(job); 52 | } 53 | } 54 | 55 | public void NavigateTo(Job item) 56 | { 57 | JobDetailsPage page = new JobDetailsPage(jobService); 58 | page.BindingContext = item; 59 | Detail = new NavigationPage(page); 60 | IsPresented = false; 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /deploy.cmd: -------------------------------------------------------------------------------- 1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off 2 | 3 | :: ---------------------- 4 | :: KUDU Deployment Script 5 | :: Version: 0.2.2 6 | :: ---------------------- 7 | 8 | :: Prerequisites 9 | :: ------------- 10 | 11 | :: Verify node.js installed 12 | where node 2>nul >nul 13 | IF %ERRORLEVEL% NEQ 0 ( 14 | echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment. 15 | goto error 16 | ) 17 | 18 | :: Setup 19 | :: ----- 20 | 21 | setlocal enabledelayedexpansion 22 | 23 | SET ARTIFACTS=%~dp0%..\artifacts 24 | 25 | IF NOT DEFINED DEPLOYMENT_SOURCE ( 26 | SET DEPLOYMENT_SOURCE=%~dp0%. 27 | ) 28 | 29 | IF NOT DEFINED DEPLOYMENT_TARGET ( 30 | SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot 31 | ) 32 | 33 | IF NOT DEFINED NEXT_MANIFEST_PATH ( 34 | SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest 35 | 36 | IF NOT DEFINED PREVIOUS_MANIFEST_PATH ( 37 | SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest 38 | ) 39 | ) 40 | 41 | IF NOT DEFINED KUDU_SYNC_CMD ( 42 | :: Install kudu sync 43 | echo Installing Kudu Sync 44 | call npm install kudusync -g --silent 45 | IF !ERRORLEVEL! NEQ 0 goto error 46 | 47 | :: Locally just running "kuduSync" would also work 48 | SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd 49 | ) 50 | IF NOT DEFINED DEPLOYMENT_TEMP ( 51 | SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random% 52 | SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true 53 | ) 54 | 55 | IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP ( 56 | IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%" 57 | mkdir "%DEPLOYMENT_TEMP%" 58 | ) 59 | 60 | IF NOT DEFINED MSBUILD_PATH ( 61 | SET MSBUILD_PATH=%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe 62 | ) 63 | 64 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 65 | :: Deployment 66 | :: ---------- 67 | 68 | echo Handling .NET Web Application deployment. 69 | 70 | :: 1. Restore NuGet packages 71 | IF /I "FieldEngineer.sln" NEQ "" ( 72 | call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\server\FieldEngineer.sln" 73 | IF !ERRORLEVEL! NEQ 0 goto error 74 | ) 75 | 76 | :: 2. Build to the temporary path 77 | IF /I "%TRY_APP_SERVICE%" NEQ "1" ( 78 | echo Default deployment (not Try App Service) 79 | 80 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\server\FieldEngineer\FieldEngineer.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS% 81 | 82 | ) ELSE ( 83 | echo Try App Service deployment 84 | 85 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\server\FieldEngineer\FieldEngineer.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release-TryAppService /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS% 86 | ) 87 | 88 | IF !ERRORLEVEL! NEQ 0 goto error 89 | 90 | IF !ERRORLEVEL! NEQ 0 goto error 91 | 92 | :: 4. Run Tests 93 | 94 | :: 5. KuduSync 95 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 96 | call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd" 97 | IF !ERRORLEVEL! NEQ 0 goto error 98 | ) 99 | 100 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 101 | 102 | :: Post deployment stub 103 | IF DEFINED POST_DEPLOYMENT_ACTION call "%POST_DEPLOYMENT_ACTION%" 104 | IF !ERRORLEVEL! NEQ 0 goto error 105 | 106 | goto end 107 | 108 | :: Execute command routine that will echo out when error 109 | :ExecuteCmd 110 | setlocal 111 | set _CMD_=%* 112 | call %_CMD_% 113 | if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_% 114 | exit /b %ERRORLEVEL% 115 | 116 | :error 117 | endlocal 118 | echo An error has occurred during web site deployment. 119 | call :exitSetErrorLevel 120 | call :exitFromFunction 2>nul 121 | 122 | :exitSetErrorLevel 123 | exit /b 1 124 | 125 | :exitFromFunction 126 | () 127 | 128 | :end 129 | endlocal 130 | echo Finished successfully. 131 | 132 | 133 | -------------------------------------------------------------------------------- /server/FieldEngineer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FieldEngineer", "FieldEngineer\FieldEngineer.csproj", "{1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug-TryAppService|Any CPU = Debug-TryAppService|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | Release-TryAppService|Any CPU = Release-TryAppService|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Debug-TryAppService|Any CPU.ActiveCfg = Debug-TryAppService|Any CPU 19 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Debug-TryAppService|Any CPU.Build.0 = Debug-TryAppService|Any CPU 20 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Release-TryAppService|Any CPU.ActiveCfg = Release-TryAppService|Any CPU 23 | {1BE26A6D-35F3-4594-AEC5-4DAF9C1BDE20}.Release-TryAppService|Any CPU.Build.0 = Release-TryAppService|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /server/FieldEngineer/App_Data/Placeholder.txt: -------------------------------------------------------------------------------- 1 | This is a placeholder file so that the App_Data folder is created during deployment. App_Data is where the SQL CE database is stored. -------------------------------------------------------------------------------- /server/FieldEngineer/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace FieldEngineer 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js")); 24 | 25 | bundles.Add(new StyleBundle("~/Content/css").Include( 26 | "~/Content/bootstrap.css", 27 | "~/Content/site.css")); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/FieldEngineer/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace FieldEngineer 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/FieldEngineer/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace FieldEngineer 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | 16 | routes.MapRoute( 17 | name: "Default", 18 | url: "{controller}/{action}/{id}", 19 | defaults: new { action = "Index", id = UrlParameter.Optional } 20 | ); 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /server/FieldEngineer/App_Start/Startup.MobileApp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Web.Http; 5 | using System.Web.Mvc; 6 | using System.Web.Optimization; 7 | using System.Web.Routing; 8 | using FieldEngineerLiteService.Models; 9 | using Microsoft.Azure.Mobile.Server.Config; 10 | using Owin; 11 | 12 | namespace FieldEngineer 13 | { 14 | public partial class Startup 15 | { 16 | public static void ConfigureMobileApp(IAppBuilder app) 17 | { 18 | HttpConfiguration config = new HttpConfiguration(); 19 | 20 | config.EnableSystemDiagnosticsTracing(); 21 | 22 | AreaRegistration.RegisterAllAreas(); 23 | RouteConfig.RegisterRoutes(RouteTable.Routes); 24 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 25 | BundleConfig.RegisterBundles(BundleTable.Bundles); 26 | 27 | new MobileAppConfiguration() 28 | .UseDefaultConfiguration() 29 | .ApplyTo(config); 30 | 31 | 32 | Database.SetInitializer(new JobDbContextInitializer()); 33 | 34 | app.UseWebApi(config); 35 | 36 | // ---------------------------------------- 37 | // UseDefaultConfiguration() equivalent to: 38 | // ---------------------------------------- 39 | //new MobileAppConfiguration() 40 | // .AddTables( 41 | // new MobileAppTableConfiguration() 42 | // .MapTableControllers() 43 | // .AddEntityFramework()) 44 | // .MapApiControllers() 45 | // .MapLegacyCrossDomainController() 46 | // .AddMobileAppHomeController() 47 | // .AddAppServiceAuthentication() 48 | // .AddPushNotifications() 49 | // .ApplyTo(config); 50 | 51 | 52 | 53 | 54 | // To display errors in the browser during development, uncomment the following 55 | // line. Comment it out again when you deploy your service for production use. 56 | // config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; 57 | 58 | } 59 | } 60 | 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /server/FieldEngineer/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /server/FieldEngineer/Content/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.0.1 by @fat and @mdo 3 | * Copyright 2013 Twitter, Inc. 4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0 5 | * 6 | * Designed and built with all the love in the world by @mdo and @fat. 7 | */ 8 | 9 | .btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e0e0e0));background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-moz-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#2d6ca2));background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-moz-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);background-repeat:repeat-x;border-color:#2b669a;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff2d6ca2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#419641));background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);background-repeat:repeat-x;border-color:#3e8f3e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff419641',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#eb9316));background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);background-repeat:repeat-x;border-color:#e38d13;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffeb9316',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c12e2a));background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);background-repeat:repeat-x;border-color:#b92c28;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc12e2a',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#2aabd2));background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);background-repeat:repeat-x;border-color:#28a4c9;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2aabd2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.navbar-default{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fff),to(#f8f8f8));background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-moz-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);background-repeat:repeat-x;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff8f8f8',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f3f3f3));background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff3f3f3',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-gradient(linear,left 0,left 100%,from(#3c3c3c),to(#222));background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-moz-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-gradient(linear,left 0,left 100%,from(#222),to(#282828));background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-moz-linear-gradient(top,#222 0,#282828 100%);background-image:linear-gradient(to bottom,#222 0,#282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff282828',GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#c8e5bc));background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);background-repeat:repeat-x;border-color:#b2dba1;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffc8e5bc',GradientType=0)}.alert-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#b9def0));background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);background-repeat:repeat-x;border-color:#9acfea;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffb9def0',GradientType=0)}.alert-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#f8efc0));background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);background-repeat:repeat-x;border-color:#f5e79e;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fff8efc0',GradientType=0)}.alert-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#e7c3c3));background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-moz-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);background-repeat:repeat-x;border-color:#dca7a7;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffe7c3c3',GradientType=0)}.progress{background-image:-webkit-gradient(linear,left 0,left 100%,from(#ebebeb),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-moz-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb',endColorstr='#fff5f5f5',GradientType=0)}.progress-bar{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3071a9));background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-moz-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3071a9',GradientType=0)}.progress-bar-success{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5cb85c),to(#449d44));background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-moz-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c',endColorstr='#ff449d44',GradientType=0)}.progress-bar-info{background-image:-webkit-gradient(linear,left 0,left 100%,from(#5bc0de),to(#31b0d5));background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-moz-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff31b0d5',GradientType=0)}.progress-bar-warning{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f0ad4e),to(#ec971f));background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-moz-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e',endColorstr='#ffec971f',GradientType=0)}.progress-bar-danger{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9534f),to(#c9302c));background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-moz-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f',endColorstr='#ffc9302c',GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#3278b3));background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-moz-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);background-repeat:repeat-x;border-color:#3278b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff3278b3',GradientType=0)}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f5f5f5),to(#e8e8e8));background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-moz-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#ffe8e8e8',GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#428bca),to(#357ebd));background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-moz-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca',endColorstr='#ff357ebd',GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#dff0d8),to(#d0e9c6));background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-moz-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8',endColorstr='#ffd0e9c6',GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#d9edf7),to(#c4e3f3));background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-moz-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7',endColorstr='#ffc4e3f3',GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#fcf8e3),to(#faf2cc));background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-moz-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3',endColorstr='#fffaf2cc',GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-gradient(linear,left 0,left 100%,from(#f2dede),to(#ebcccc));background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-moz-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede',endColorstr='#ffebcccc',GradientType=0)}.well{background-image:-webkit-gradient(linear,left 0,left 100%,from(#e8e8e8),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-moz-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);background-repeat:repeat-x;border-color:#dcdcdc;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8',endColorstr='#fff5f5f5',GradientType=0);-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} -------------------------------------------------------------------------------- /server/FieldEngineer/Controllers/AdminController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Entity; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Net; 8 | using System.Web; 9 | using System.Web.Mvc; 10 | using FieldEngineerLiteService.DataObjects; 11 | using FieldEngineerLiteService.Models; 12 | 13 | namespace FieldEngineer.Controllers 14 | { 15 | public class AdminController : Controller 16 | { 17 | private JobDbContext db = new JobDbContext(); 18 | 19 | // GET: Admin 20 | public async Task Index() 21 | { 22 | return View(await db.JobsDbSet.ToListAsync()); 23 | } 24 | 25 | // GET: Admin/Details/5 26 | public async Task Details(string id) 27 | { 28 | if (id == null) 29 | { 30 | return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 31 | } 32 | Job job = await db.JobsDbSet.FindAsync(id); 33 | if (job == null) 34 | { 35 | return HttpNotFound(); 36 | } 37 | return View(job); 38 | } 39 | 40 | // GET: Admin/Create 41 | public ActionResult Create() 42 | { 43 | return View(); 44 | } 45 | 46 | // POST: Admin/Create 47 | // To protect from overposting attacks, please enable the specific properties you want to bind to, for 48 | // more details see http://go.microsoft.com/fwlink/?LinkId=317598. 49 | [HttpPost] 50 | [ValidateAntiForgeryToken] 51 | public async Task Create([Bind(Include = "Id,AgentId,JobNumber,Title,StartTime,EndTime,Status,CustomerName,CustomerAddress,CustomerPhoneNumber,WorkPerformed,Version,CreatedAt,UpdatedAt,Deleted")] Job job) 52 | { 53 | if (ModelState.IsValid) 54 | { 55 | db.JobsDbSet.Add(job); 56 | await db.SaveChangesAsync(); 57 | return RedirectToAction("Index"); 58 | } 59 | 60 | return View(job); 61 | } 62 | 63 | // GET: Admin/Edit/5 64 | public async Task Edit(string id) 65 | { 66 | if (id == null) 67 | { 68 | return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 69 | } 70 | Job job = await db.JobsDbSet.FindAsync(id); 71 | if (job == null) 72 | { 73 | return HttpNotFound(); 74 | } 75 | return View(job); 76 | } 77 | 78 | // POST: Admin/Edit/5 79 | // To protect from overposting attacks, please enable the specific properties you want to bind to, for 80 | // more details see http://go.microsoft.com/fwlink/?LinkId=317598. 81 | [HttpPost] 82 | [ValidateAntiForgeryToken] 83 | public async Task Edit([Bind(Include = "Id,AgentId,JobNumber,Title,StartTime,EndTime,Status,CustomerName,CustomerAddress,CustomerPhoneNumber,WorkPerformed,CreatedAt,UpdatedAt,Version,Deleted")] Job job) 84 | { 85 | if (ModelState.IsValid) 86 | { 87 | db.Entry(job).State = EntityState.Modified; 88 | await db.SaveChangesAsync(); 89 | return RedirectToAction("Index"); 90 | } 91 | return View(job); 92 | } 93 | 94 | // GET: Admin/Delete/5 95 | public async Task Delete(string id) 96 | { 97 | if (id == null) 98 | { 99 | return new HttpStatusCodeResult(HttpStatusCode.BadRequest); 100 | } 101 | Job job = await db.JobsDbSet.FindAsync(id); 102 | if (job == null) 103 | { 104 | return HttpNotFound(); 105 | } 106 | return View(job); 107 | } 108 | 109 | // POST: Admin/Delete/5 110 | [HttpPost, ActionName("Delete")] 111 | [ValidateAntiForgeryToken] 112 | public async Task DeleteConfirmed(string id) 113 | { 114 | Job job = await db.JobsDbSet.FindAsync(id); 115 | db.JobsDbSet.Remove(job); 116 | await db.SaveChangesAsync(); 117 | return RedirectToAction("Index"); 118 | } 119 | 120 | protected override void Dispose(bool disposing) 121 | { 122 | if (disposing) 123 | { 124 | db.Dispose(); 125 | } 126 | base.Dispose(disposing); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /server/FieldEngineer/Controllers/JobController.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using System.Web.Http; 4 | using System.Web.Http.Controllers; 5 | using System.Web.Http.OData; 6 | using Microsoft.Azure.Mobile.Server; 7 | using FieldEngineerLiteService.DataObjects; 8 | using FieldEngineerLiteService.Models; 9 | 10 | namespace FieldEngineerLiteService.Controllers 11 | { 12 | public class JobController : TableController 13 | { 14 | protected override void Initialize(HttpControllerContext controllerContext) 15 | { 16 | base.Initialize(controllerContext); 17 | JobDbContext context = new JobDbContext(); 18 | DomainManager = new EntityDomainManager(context, Request, enableSoftDelete: true); 19 | } 20 | 21 | // GET tables/Job 22 | public IQueryable GetAllJobs() 23 | { 24 | return Query(); 25 | } 26 | 27 | // GET tables/Job/48D68C86-6EA6-4C25-AA33-223FC9A27959 28 | public SingleResult GetJob(string id) 29 | { 30 | return Lookup(id); 31 | } 32 | 33 | // PATCH tables/Job/48D68C86-6EA6-4C25-AA33-223FC9A27959 34 | public async Task PatchJob(string id, Delta patch) 35 | { 36 | Job job = patch.GetEntity(); // get new value 37 | return await UpdateAsync(id, patch); 38 | } 39 | 40 | // POST tables/Job 41 | public async Task PostJob(Job item) 42 | { 43 | Job current = await InsertAsync(item); 44 | return CreatedAtRoute("Tables", new { id = current.Id }, current); 45 | } 46 | 47 | // DELETE tables/Job/48D68C86-6EA6-4C25-AA33-223FC9A27959 48 | public Task DeleteJob(string id) 49 | { 50 | return DeleteAsync(id); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /server/FieldEngineer/DataObjects/Job.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Mobile.Server; 2 | using Microsoft.Azure.Mobile.Server.Tables; 3 | using System; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | 6 | namespace FieldEngineerLiteService.DataObjects 7 | { 8 | public class Job : ITableData 9 | { 10 | public string AgentId { get; set; } 11 | 12 | public string JobNumber { get; set; } 13 | 14 | public string Title { get; set; } 15 | public string StartTime { get; set; } 16 | public string EndTime { get; set; } 17 | public string Status { get; set; } 18 | 19 | public string CustomerName { get; set; } 20 | public string CustomerAddress { get; set; } 21 | public string CustomerPhoneNumber { get; set; } 22 | 23 | public string WorkPerformed { get; set; } 24 | 25 | #region System properties 26 | public string Id { get; set; } 27 | 28 | public byte[] Version { get; set; } 29 | 30 | #if TRY_APP_SERVICE 31 | [NotMapped] 32 | #endif 33 | public DateTimeOffset? CreatedAt { get; set; } 34 | 35 | #if TRY_APP_SERVICE 36 | [NotMapped] 37 | #endif 38 | public DateTimeOffset? UpdatedAt { get; set; } 39 | 40 | public bool Deleted { get; set; } 41 | #endregion 42 | 43 | #if TRY_APP_SERVICE 44 | // SQL Compact Edition does not support the DateTimeOffset type. 45 | // These are simple backing properties that store these values using the DateTime type instead. 46 | public DateTime __createdAtDateTime 47 | { 48 | get { return CreatedAt.HasValue ? CreatedAt.Value.DateTime : DateTime.Now; } 49 | set { CreatedAt = value; } 50 | } 51 | 52 | public DateTime __updatedAtDateTime 53 | { 54 | get { return UpdatedAt.HasValue ? UpdatedAt.Value.DateTime : DateTime.Now; } 55 | set { UpdatedAt = value; } 56 | } 57 | #endif 58 | 59 | public Job() 60 | { 61 | Id = Guid.NewGuid().ToString("N"); 62 | AgentId = "Carla Davis"; 63 | JobNumber = ""; 64 | Status = "Not Started"; 65 | } 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /server/FieldEngineer/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="FieldEngineer.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /server/FieldEngineer/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using System.Web.Routing; 3 | using System.Web.Mvc; 4 | 5 | using System.Web.Optimization; 6 | 7 | namespace FieldEngineer 8 | { 9 | public class WebApiApplication : System.Web.HttpApplication 10 | { 11 | protected void Application_Start() 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /server/FieldEngineer/Models/JobDbContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using System.Data.Entity.ModelConfiguration.Conventions; 3 | using System.Linq; 4 | using Microsoft.Azure.Mobile.Server; 5 | using Microsoft.Azure.Mobile.Server.Tables; 6 | using FieldEngineerLiteService.DataObjects; 7 | using System.Collections.Generic; 8 | 9 | namespace FieldEngineerLiteService.Models 10 | { 11 | public class JobDbContext : DbContext 12 | { 13 | // You can add custom code to this file. Changes will not be overwritten. 14 | // 15 | // If you want Entity Framework to alter your database 16 | // automatically whenever you change your model schema, please use data migrations. 17 | // For more information refer to the documentation: 18 | // http://msdn.microsoft.com/en-us/data/jj591621.aspx 19 | // 20 | // To enable Entity Framework migrations in the cloud, please ensure that the 21 | // service name, set by the 'MS_MobileServiceName' AppSettings in the local 22 | // Web.config, is the same as the service name when hosted in Azure. 23 | 24 | #if TRY_APP_SERVICE 25 | private const string connectionStringName = "Name=MS_TryAppService_TableConnectionString"; 26 | #else 27 | private const string connectionStringName = "Name=MS_TableConnectionString"; 28 | #endif 29 | 30 | public JobDbContext() 31 | : base(connectionStringName) 32 | { 33 | } 34 | 35 | public DbSet JobsDbSet { get; set; } 36 | 37 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 38 | { 39 | modelBuilder.Conventions.Add( 40 | new AttributeToColumnAnnotationConvention( 41 | "ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString())); 42 | 43 | #if TRY_APP_SERVICE 44 | // The EntityFramework SQL Compact provider does not support the DateTimeOffset type 45 | modelBuilder.Types().Configure(x => x.Ignore(prop => prop.CreatedAt)); 46 | modelBuilder.Types().Configure(x => x.Ignore(prop => prop.UpdatedAt)); 47 | #endif 48 | 49 | } 50 | } 51 | 52 | #if TRY_APP_SERVICE 53 | public class JobDbContextInitializer : DropCreateDatabaseAlways 54 | #else 55 | public class JobDbContextInitializer : CreateDatabaseIfNotExists 56 | #endif 57 | { 58 | protected override void Seed(JobDbContext context) 59 | { 60 | List jobs = new List 61 | { 62 | new Job {Title = "Install Deluxe DVR box", CustomerName = "Chris Anderson", CustomerAddress = "123 Fake St", CustomerPhoneNumber = "3079876543"}, 63 | new Job {Title = "Cable box outside is missing", CustomerName = "Kirill Gavrylyuk", CustomerAddress = "987 Real St", CustomerPhoneNumber = "4251234567"}, 64 | new Job {Title = "Add Cable to new room", CustomerName = "Donna Malayeri", CustomerAddress = "456 3rd Dimension", CustomerPhoneNumber = "7860987432"}, 65 | new Job {Title = "Approve free Cable for my employees", CustomerName = "Bill Staples", CustomerAddress = "777 Best Boss Dr.", CustomerPhoneNumber = "4259829322"} 66 | }; 67 | 68 | foreach (Job job in jobs) 69 | { 70 | context.Set().Add(job); 71 | } 72 | 73 | base.Seed(context); 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /server/FieldEngineer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FieldEngineer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FieldEngineer")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a5aa681d-2788-454c-8a29-bf6c203a01d3")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /server/FieldEngineer/Scripts/jquery.validate.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! jQuery Validation Plugin - v1.11.1 - 3/22/2013\n* https://github.com/jzaefferer/jquery-validation 16 | * Copyright (c) 2013 Jörn Zaefferer; Licensed MIT */(function(t){t.extend(t.fn,{validate:function(e){if(!this.length)return e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."),void 0;var i=t.data(this[0],"validator");return i?i:(this.attr("novalidate","novalidate"),i=new t.validator(e,this[0]),t.data(this[0],"validator",i),i.settings.onsubmit&&(this.validateDelegate(":submit","click",function(e){i.settings.submitHandler&&(i.submitButton=e.target),t(e.target).hasClass("cancel")&&(i.cancelSubmit=!0),void 0!==t(e.target).attr("formnovalidate")&&(i.cancelSubmit=!0)}),this.submit(function(e){function s(){var s;return i.settings.submitHandler?(i.submitButton&&(s=t("").attr("name",i.submitButton.name).val(t(i.submitButton).val()).appendTo(i.currentForm)),i.settings.submitHandler.call(i,i.currentForm,e),i.submitButton&&s.remove(),!1):!0}return i.settings.debug&&e.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,s()):i.form()?i.pendingRequest?(i.formSubmitted=!0,!1):s():(i.focusInvalid(),!1)})),i)},valid:function(){if(t(this[0]).is("form"))return this.validate().form();var e=!0,i=t(this[0].form).validate();return this.each(function(){e=e&&i.element(this)}),e},removeAttrs:function(e){var i={},s=this;return t.each(e.split(/\s/),function(t,e){i[e]=s.attr(e),s.removeAttr(e)}),i},rules:function(e,i){var s=this[0];if(e){var r=t.data(s.form,"validator").settings,n=r.rules,a=t.validator.staticRules(s);switch(e){case"add":t.extend(a,t.validator.normalizeRule(i)),delete a.messages,n[s.name]=a,i.messages&&(r.messages[s.name]=t.extend(r.messages[s.name],i.messages));break;case"remove":if(!i)return delete n[s.name],a;var u={};return t.each(i.split(/\s/),function(t,e){u[e]=a[e],delete a[e]}),u}}var o=t.validator.normalizeRules(t.extend({},t.validator.classRules(s),t.validator.attributeRules(s),t.validator.dataRules(s),t.validator.staticRules(s)),s);if(o.required){var l=o.required;delete o.required,o=t.extend({required:l},o)}return o}}),t.extend(t.expr[":"],{blank:function(e){return!t.trim(""+t(e).val())},filled:function(e){return!!t.trim(""+t(e).val())},unchecked:function(e){return!t(e).prop("checked")}}),t.validator=function(e,i){this.settings=t.extend(!0,{},t.validator.defaults,e),this.currentForm=i,this.init()},t.validator.format=function(e,i){return 1===arguments.length?function(){var i=t.makeArray(arguments);return i.unshift(e),t.validator.format.apply(this,i)}:(arguments.length>2&&i.constructor!==Array&&(i=t.makeArray(arguments).slice(1)),i.constructor!==Array&&(i=[i]),t.each(i,function(t,i){e=e.replace(RegExp("\\{"+t+"\\}","g"),function(){return i})}),e)},t.extend(t.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",validClass:"valid",errorElement:"label",focusInvalid:!0,errorContainer:t([]),errorLabelContainer:t([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(t){this.lastActive=t,this.settings.focusCleanup&&!this.blockFocusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,t,this.settings.errorClass,this.settings.validClass),this.addWrapper(this.errorsFor(t)).hide())},onfocusout:function(t){this.checkable(t)||!(t.name in this.submitted)&&this.optional(t)||this.element(t)},onkeyup:function(t,e){(9!==e.which||""!==this.elementValue(t))&&(t.name in this.submitted||t===this.lastElement)&&this.element(t)},onclick:function(t){t.name in this.submitted?this.element(t):t.parentNode.name in this.submitted&&this.element(t.parentNode)},highlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).addClass(i).removeClass(s):t(e).addClass(i).removeClass(s)},unhighlight:function(e,i,s){"radio"===e.type?this.findByName(e.name).removeClass(i).addClass(s):t(e).removeClass(i).addClass(s)}},setDefaults:function(e){t.extend(t.validator.defaults,e)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",creditcard:"Please enter a valid credit card number.",equalTo:"Please enter the same value again.",maxlength:t.validator.format("Please enter no more than {0} characters."),minlength:t.validator.format("Please enter at least {0} characters."),rangelength:t.validator.format("Please enter a value between {0} and {1} characters long."),range:t.validator.format("Please enter a value between {0} and {1}."),max:t.validator.format("Please enter a value less than or equal to {0}."),min:t.validator.format("Please enter a value greater than or equal to {0}.")},autoCreateRanges:!1,prototype:{init:function(){function e(e){var i=t.data(this[0].form,"validator"),s="on"+e.type.replace(/^validate/,"");i.settings[s]&&i.settings[s].call(i,this[0],e)}this.labelContainer=t(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||t(this.currentForm),this.containers=t(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var i=this.groups={};t.each(this.settings.groups,function(e,s){"string"==typeof s&&(s=s.split(/\s/)),t.each(s,function(t,s){i[s]=e})});var s=this.settings.rules;t.each(s,function(e,i){s[e]=t.validator.normalizeRule(i)}),t(this.currentForm).validateDelegate(":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'] ,[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'] ","focusin focusout keyup",e).validateDelegate("[type='radio'], [type='checkbox'], select, option","click",e),this.settings.invalidHandler&&t(this.currentForm).bind("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),t.extend(this.submitted,this.errorMap),this.invalid=t.extend({},this.errorMap),this.valid()||t(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var t=0,e=this.currentElements=this.elements();e[t];t++)this.check(e[t]);return this.valid()},element:function(e){e=this.validationTargetFor(this.clean(e)),this.lastElement=e,this.prepareElement(e),this.currentElements=t(e);var i=this.check(e)!==!1;return i?delete this.invalid[e.name]:this.invalid[e.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),i},showErrors:function(e){if(e){t.extend(this.errorMap,e),this.errorList=[];for(var i in e)this.errorList.push({message:e[i],element:this.findByName(i)[0]});this.successList=t.grep(this.successList,function(t){return!(t.name in e)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){t.fn.resetForm&&t(this.currentForm).resetForm(),this.submitted={},this.lastElement=null,this.prepareForm(),this.hideErrors(),this.elements().removeClass(this.settings.errorClass).removeData("previousValue")},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(t){var e=0;for(var i in t)e++;return e},hideErrors:function(){this.addWrapper(this.toHide).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{t(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(e){}},findLastActive:function(){var e=this.lastActive;return e&&1===t.grep(this.errorList,function(t){return t.element.name===e.name}).length&&e},elements:function(){var e=this,i={};return t(this.currentForm).find("input, select, textarea").not(":submit, :reset, :image, [disabled]").not(this.settings.ignore).filter(function(){return!this.name&&e.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.name in i||!e.objectLength(t(this).rules())?!1:(i[this.name]=!0,!0)})},clean:function(e){return t(e)[0]},errors:function(){var e=this.settings.errorClass.replace(" ",".");return t(this.settings.errorElement+"."+e,this.errorContext)},reset:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=t([]),this.toHide=t([]),this.currentElements=t([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(t){this.reset(),this.toHide=this.errorsFor(t)},elementValue:function(e){var i=t(e).attr("type"),s=t(e).val();return"radio"===i||"checkbox"===i?t("input[name='"+t(e).attr("name")+"']:checked").val():"string"==typeof s?s.replace(/\r/g,""):s},check:function(e){e=this.validationTargetFor(this.clean(e));var i,s=t(e).rules(),r=!1,n=this.elementValue(e);for(var a in s){var u={method:a,parameters:s[a]};try{if(i=t.validator.methods[a].call(this,n,e,u.parameters),"dependency-mismatch"===i){r=!0;continue}if(r=!1,"pending"===i)return this.toHide=this.toHide.not(this.errorsFor(e)),void 0;if(!i)return this.formatAndAdd(e,u),!1}catch(o){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+e.id+", check the '"+u.method+"' method.",o),o}}return r?void 0:(this.objectLength(s)&&this.successList.push(e),!0)},customDataMessage:function(e,i){return t(e).data("msg-"+i.toLowerCase())||e.attributes&&t(e).attr("data-msg-"+i.toLowerCase())},customMessage:function(t,e){var i=this.settings.messages[t];return i&&(i.constructor===String?i:i[e])},findDefined:function(){for(var t=0;arguments.length>t;t++)if(void 0!==arguments[t])return arguments[t];return void 0},defaultMessage:function(e,i){return this.findDefined(this.customMessage(e.name,i),this.customDataMessage(e,i),!this.settings.ignoreTitle&&e.title||void 0,t.validator.messages[i],"Warning: No message defined for "+e.name+"")},formatAndAdd:function(e,i){var s=this.defaultMessage(e,i.method),r=/\$?\{(\d+)\}/g;"function"==typeof s?s=s.call(this,i.parameters,e):r.test(s)&&(s=t.validator.format(s.replace(r,"{$1}"),i.parameters)),this.errorList.push({message:s,element:e}),this.errorMap[e.name]=s,this.submitted[e.name]=s},addWrapper:function(t){return this.settings.wrapper&&(t=t.add(t.parent(this.settings.wrapper))),t},defaultShowErrors:function(){var t,e;for(t=0;this.errorList[t];t++){var i=this.errorList[t];this.settings.highlight&&this.settings.highlight.call(this,i.element,this.settings.errorClass,this.settings.validClass),this.showLabel(i.element,i.message)}if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(t=0;this.successList[t];t++)this.showLabel(this.successList[t]);if(this.settings.unhighlight)for(t=0,e=this.validElements();e[t];t++)this.settings.unhighlight.call(this,e[t],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return t(this.errorList).map(function(){return this.element})},showLabel:function(e,i){var s=this.errorsFor(e);s.length?(s.removeClass(this.settings.validClass).addClass(this.settings.errorClass),s.html(i)):(s=t("<"+this.settings.errorElement+">").attr("for",this.idOrName(e)).addClass(this.settings.errorClass).html(i||""),this.settings.wrapper&&(s=s.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.append(s).length||(this.settings.errorPlacement?this.settings.errorPlacement(s,t(e)):s.insertAfter(e))),!i&&this.settings.success&&(s.text(""),"string"==typeof this.settings.success?s.addClass(this.settings.success):this.settings.success(s,e)),this.toShow=this.toShow.add(s)},errorsFor:function(e){var i=this.idOrName(e);return this.errors().filter(function(){return t(this).attr("for")===i})},idOrName:function(t){return this.groups[t.name]||(this.checkable(t)?t.name:t.id||t.name)},validationTargetFor:function(t){return this.checkable(t)&&(t=this.findByName(t.name).not(this.settings.ignore)[0]),t},checkable:function(t){return/radio|checkbox/i.test(t.type)},findByName:function(e){return t(this.currentForm).find("[name='"+e+"']")},getLength:function(e,i){switch(i.nodeName.toLowerCase()){case"select":return t("option:selected",i).length;case"input":if(this.checkable(i))return this.findByName(i.name).filter(":checked").length}return e.length},depend:function(t,e){return this.dependTypes[typeof t]?this.dependTypes[typeof t](t,e):!0},dependTypes:{"boolean":function(t){return t},string:function(e,i){return!!t(e,i.form).length},"function":function(t,e){return t(e)}},optional:function(e){var i=this.elementValue(e);return!t.validator.methods.required.call(this,i,e)&&"dependency-mismatch"},startRequest:function(t){this.pending[t.name]||(this.pendingRequest++,this.pending[t.name]=!0)},stopRequest:function(e,i){this.pendingRequest--,0>this.pendingRequest&&(this.pendingRequest=0),delete this.pending[e.name],i&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(t(this.currentForm).submit(),this.formSubmitted=!1):!i&&0===this.pendingRequest&&this.formSubmitted&&(t(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e){return t.data(e,"previousValue")||t.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,"remote")})}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,i){e.constructor===String?this.classRuleSettings[e]=i:t.extend(this.classRuleSettings,e)},classRules:function(e){var i={},s=t(e).attr("class");return s&&t.each(s.split(" "),function(){this in t.validator.classRuleSettings&&t.extend(i,t.validator.classRuleSettings[this])}),i},attributeRules:function(e){var i={},s=t(e),r=s[0].getAttribute("type");for(var n in t.validator.methods){var a;"required"===n?(a=s.get(0).getAttribute(n),""===a&&(a=!0),a=!!a):a=s.attr(n),/min|max/.test(n)&&(null===r||/number|range|text/.test(r))&&(a=Number(a)),a?i[n]=a:r===n&&"range"!==r&&(i[n]=!0)}return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var i,s,r={},n=t(e);for(i in t.validator.methods)s=n.data("rule-"+i.toLowerCase()),void 0!==s&&(r[i]=s);return r},staticRules:function(e){var i={},s=t.data(e.form,"validator");return s.settings.rules&&(i=t.validator.normalizeRule(s.settings.rules[e.name])||{}),i},normalizeRules:function(e,i){return t.each(e,function(s,r){if(r===!1)return delete e[s],void 0;if(r.param||r.depends){var n=!0;switch(typeof r.depends){case"string":n=!!t(r.depends,i.form).length;break;case"function":n=r.depends.call(i,i)}n?e[s]=void 0!==r.param?r.param:!0:delete e[s]}}),t.each(e,function(s,r){e[s]=t.isFunction(r)?r(i):r}),t.each(["minlength","maxlength"],function(){e[this]&&(e[this]=Number(e[this]))}),t.each(["rangelength","range"],function(){var i;e[this]&&(t.isArray(e[this])?e[this]=[Number(e[this][0]),Number(e[this][1])]:"string"==typeof e[this]&&(i=e[this].split(/[\s,]+/),e[this]=[Number(i[0]),Number(i[1])]))}),t.validator.autoCreateRanges&&(e.min&&e.max&&(e.range=[e.min,e.max],delete e.min,delete e.max),e.minlength&&e.maxlength&&(e.rangelength=[e.minlength,e.maxlength],delete e.minlength,delete e.maxlength)),e},normalizeRule:function(e){if("string"==typeof e){var i={};t.each(e.split(/\s/),function(){i[this]=!0}),e=i}return e},addMethod:function(e,i,s){t.validator.methods[e]=i,t.validator.messages[e]=void 0!==s?s:t.validator.messages[e],3>i.length&&t.validator.addClassRules(e,t.validator.normalizeRule(e))},methods:{required:function(e,i,s){if(!this.depend(s,i))return"dependency-mismatch";if("select"===i.nodeName.toLowerCase()){var r=t(i).val();return r&&r.length>0}return this.checkable(i)?this.getLength(e,i)>0:t.trim(e).length>0},email:function(t,e){return this.optional(e)||/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(t)},url:function(t,e){return this.optional(e)||/^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(t)},date:function(t,e){return this.optional(e)||!/Invalid|NaN/.test(""+new Date(t))},dateISO:function(t,e){return this.optional(e)||/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(t)},number:function(t,e){return this.optional(e)||/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(t)},digits:function(t,e){return this.optional(e)||/^\d+$/.test(t)},creditcard:function(t,e){if(this.optional(e))return"dependency-mismatch";if(/[^0-9 \-]+/.test(t))return!1;var i=0,s=0,r=!1;t=t.replace(/\D/g,"");for(var n=t.length-1;n>=0;n--){var a=t.charAt(n);s=parseInt(a,10),r&&(s*=2)>9&&(s-=9),i+=s,r=!r}return 0===i%10},minlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s},maxlength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||s>=r},rangelength:function(e,i,s){var r=t.isArray(e)?e.length:this.getLength(t.trim(e),i);return this.optional(i)||r>=s[0]&&s[1]>=r},min:function(t,e,i){return this.optional(e)||t>=i},max:function(t,e,i){return this.optional(e)||i>=t},range:function(t,e,i){return this.optional(e)||t>=i[0]&&i[1]>=t},equalTo:function(e,i,s){var r=t(s);return this.settings.onfocusout&&r.unbind(".validate-equalTo").bind("blur.validate-equalTo",function(){t(i).valid()}),e===r.val()},remote:function(e,i,s){if(this.optional(i))return"dependency-mismatch";var r=this.previousValue(i);if(this.settings.messages[i.name]||(this.settings.messages[i.name]={}),r.originalMessage=this.settings.messages[i.name].remote,this.settings.messages[i.name].remote=r.message,s="string"==typeof s&&{url:s}||s,r.old===e)return r.valid;r.old=e;var n=this;this.startRequest(i);var a={};return a[i.name]=e,t.ajax(t.extend(!0,{url:s,mode:"abort",port:"validate"+i.name,dataType:"json",data:a,success:function(s){n.settings.messages[i.name].remote=r.originalMessage;var a=s===!0||"true"===s;if(a){var u=n.formSubmitted;n.prepareElement(i),n.formSubmitted=u,n.successList.push(i),delete n.invalid[i.name],n.showErrors()}else{var o={},l=s||n.defaultMessage(i,"remote");o[i.name]=r.message=t.isFunction(l)?l(e):l,n.invalid[i.name]=!0,n.showErrors(o)}r.valid=a,n.stopRequest(i,a)}},s)),"pending"}}}),t.format=t.validator.format})(jQuery),function(t){var e={};if(t.ajaxPrefilter)t.ajaxPrefilter(function(t,i,s){var r=t.port;"abort"===t.mode&&(e[r]&&e[r].abort(),e[r]=s)});else{var i=t.ajax;t.ajax=function(s){var r=("mode"in s?s:t.ajaxSettings).mode,n=("port"in s?s:t.ajaxSettings).port;return"abort"===r?(e[n]&&e[n].abort(),e[n]=i.apply(this,arguments),e[n]):i.apply(this,arguments)}}}(jQuery),function(t){t.extend(t.fn,{validateDelegate:function(e,i,s){return this.bind(i,function(i){var r=t(i.target);return r.is(e)?s.apply(r,arguments):void 0})}})}(jQuery); -------------------------------------------------------------------------------- /server/FieldEngineer/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this),c="__jquery_unobtrusive_validation_form_reset";if(b.data(c))return;b.data(c,true);try{b.data("validator").resetForm()}finally{b.removeData(c)}b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(b){var c=a(b),f=c.data(e),i=a.proxy(n,b),g=d.unobtrusive.options||{},h=function(e,d){var c=g[e];c&&a.isFunction(c)&&c.apply(b,d)};if(!f){f={options:{errorClass:g.errorClass||"input-validation-error",errorElement:g.errorElement||"span",errorPlacement:function(){m.apply(b,arguments);h("errorPlacement",arguments)},invalidHandler:function(){l.apply(b,arguments);h("invalidHandler",arguments)},messages:{},rules:{},success:function(){k.apply(b,arguments);h("success",arguments)}},attachValidation:function(){c.off("reset."+e,i).on("reset."+e,i).validate(this.options)},validate:function(){c.validate();return c.valid()}};c.data(e,f)}return f}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(c){var b=a(c),e=b.parents().addBack().filter("form").add(b.find("form")).has("[data-val=true]");b.find("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});e.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){var d=a(b.form).find(":input").filter("[name='"+f(c)+"']");return d.is(":checkbox")?d.filter(":checked").val()||d.filter(":hidden").val()||"":d.is(":radio")?d.filter(":checked").val()||"":d.val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /server/FieldEngineer/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Owin; 4 | using Owin; 5 | 6 | [assembly: OwinStartup(typeof(FieldEngineer.Startup))] 7 | 8 | namespace FieldEngineer 9 | { 10 | public partial class Startup 11 | { 12 | public void Configuration(IAppBuilder app) 13 | { 14 | ConfigureMobileApp(app); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Admin/Create.cshtml: -------------------------------------------------------------------------------- 1 | @model FieldEngineerLiteService.DataObjects.Job 2 | 3 | @{ 4 | ViewBag.Title = "Create"; 5 | } 6 | 7 |

    Create

    8 | 9 | 10 | @using (Html.BeginForm()) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
    15 |

    Job

    16 |
    17 | @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 18 | 19 |
    20 | @Html.LabelFor(model => model.AgentId, htmlAttributes: new { @class = "control-label col-md-2" }) 21 |
    22 | 23 | @Html.DropDownList("Agent", new List() 24 | { new SelectListItem() 25 | { 26 | Text = "Bob Johnson", Value = "Bob Johnson" 27 | }, 28 | new SelectListItem() 29 | { 30 | Text = "Carla Davis", Value = "Carla Davis" 31 | } 32 | 33 | }) 34 | @*@Html.EditorFor(model => model.AgentId, new { htmlAttributes = new { @class = "form-control" } })*@ 35 | @Html.ValidationMessageFor(model => model.AgentId, "", new { @class = "text-danger" }) 36 |
    37 |
    38 | 39 | 40 |
    41 | @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" }) 42 |
    43 | @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } }) 44 | @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" }) 45 |
    46 |
    47 | 48 | 49 |
    50 | @Html.LabelFor(model => model.StartTime, htmlAttributes: new { @class = "control-label col-md-2" }) 51 |
    52 | @Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { @class = "form-control" } }) 53 | @Html.ValidationMessageFor(model => model.StartTime, "", new { @class = "text-danger" }) 54 |
    55 |
    56 | 57 |
    58 | @Html.LabelFor(model => model.EndTime, htmlAttributes: new { @class = "control-label col-md-2" }) 59 |
    60 | @Html.EditorFor(model => model.EndTime, new { htmlAttributes = new { @class = "form-control" } }) 61 | @Html.ValidationMessageFor(model => model.EndTime, "", new { @class = "text-danger" }) 62 |
    63 |
    64 | 65 |
    66 | @Html.LabelFor(model => model.CustomerName, htmlAttributes: new { @class = "control-label col-md-2" }) 67 |
    68 | @Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { @class = "form-control" } }) 69 | @Html.ValidationMessageFor(model => model.CustomerName, "", new { @class = "text-danger" }) 70 |
    71 |
    72 | 73 |
    74 | @Html.LabelFor(model => model.CustomerAddress, htmlAttributes: new { @class = "control-label col-md-2" }) 75 |
    76 | @Html.EditorFor(model => model.CustomerAddress, new { htmlAttributes = new { @class = "form-control" } }) 77 | @Html.ValidationMessageFor(model => model.CustomerAddress, "", new { @class = "text-danger" }) 78 |
    79 |
    80 | 81 |
    82 | @Html.LabelFor(model => model.CustomerPhoneNumber, htmlAttributes: new { @class = "control-label col-md-2" }) 83 |
    84 | @Html.EditorFor(model => model.CustomerPhoneNumber, new { htmlAttributes = new { @class = "form-control" } }) 85 | @Html.ValidationMessageFor(model => model.CustomerPhoneNumber, "", new { @class = "text-danger" }) 86 |
    87 |
    88 | 89 |
    90 |
    91 | 92 |
    93 |
    94 |
    95 | } 96 | 97 |
    98 | @Html.ActionLink("Back to List", "Index") 99 |
    100 | 101 | @section Scripts { 102 | @Scripts.Render("~/bundles/jqueryval") 103 | } 104 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Admin/Delete.cshtml: -------------------------------------------------------------------------------- 1 | @model FieldEngineerLiteService.DataObjects.Job 2 | 3 | @{ 4 | ViewBag.Title = "Delete"; 5 | } 6 | 7 |

    Delete

    8 | 9 |

    Are you sure you want to delete this?

    10 |
    11 |

    Job

    12 |
    13 |
    14 |
    15 | @Html.DisplayNameFor(model => model.AgentId) 16 |
    17 | 18 |
    19 | @Html.DisplayFor(model => model.AgentId) 20 |
    21 | 22 |
    23 | @Html.DisplayNameFor(model => model.JobNumber) 24 |
    25 | 26 |
    27 | @Html.DisplayFor(model => model.JobNumber) 28 |
    29 | 30 |
    31 | @Html.DisplayNameFor(model => model.Title) 32 |
    33 | 34 |
    35 | @Html.DisplayFor(model => model.Title) 36 |
    37 | 38 |
    39 | @Html.DisplayNameFor(model => model.StartTime) 40 |
    41 | 42 |
    43 | @Html.DisplayFor(model => model.StartTime) 44 |
    45 | 46 |
    47 | @Html.DisplayNameFor(model => model.EndTime) 48 |
    49 | 50 |
    51 | @Html.DisplayFor(model => model.EndTime) 52 |
    53 | 54 |
    55 | @Html.DisplayNameFor(model => model.Status) 56 |
    57 | 58 |
    59 | @Html.DisplayFor(model => model.Status) 60 |
    61 | 62 |
    63 | @Html.DisplayNameFor(model => model.CustomerName) 64 |
    65 | 66 |
    67 | @Html.DisplayFor(model => model.CustomerName) 68 |
    69 | 70 |
    71 | @Html.DisplayNameFor(model => model.CustomerAddress) 72 |
    73 | 74 |
    75 | @Html.DisplayFor(model => model.CustomerAddress) 76 |
    77 | 78 |
    79 | @Html.DisplayNameFor(model => model.CustomerPhoneNumber) 80 |
    81 | 82 |
    83 | @Html.DisplayFor(model => model.CustomerPhoneNumber) 84 |
    85 | 86 |
    87 | @Html.DisplayNameFor(model => model.WorkPerformed) 88 |
    89 | 90 |
    91 | @Html.DisplayFor(model => model.WorkPerformed) 92 |
    93 | 94 |
    95 | @Html.DisplayNameFor(model => model.Version) 96 |
    97 | 98 |
    99 | @Html.DisplayFor(model => model.Version) 100 |
    101 | 102 |
    103 | @Html.DisplayNameFor(model => model.CreatedAt) 104 |
    105 | 106 |
    107 | @Html.DisplayFor(model => model.CreatedAt) 108 |
    109 | 110 |
    111 | @Html.DisplayNameFor(model => model.UpdatedAt) 112 |
    113 | 114 |
    115 | @Html.DisplayFor(model => model.UpdatedAt) 116 |
    117 | 118 |
    119 | @Html.DisplayNameFor(model => model.Deleted) 120 |
    121 | 122 |
    123 | @Html.DisplayFor(model => model.Deleted) 124 |
    125 | 126 |
    127 | 128 | @using (Html.BeginForm()) { 129 | @Html.AntiForgeryToken() 130 | 131 |
    132 | | 133 | @Html.ActionLink("Back to List", "Index") 134 |
    135 | } 136 |
    137 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Admin/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model FieldEngineerLiteService.DataObjects.Job 2 | 3 | @{ 4 | ViewBag.Title = "Details"; 5 | } 6 | 7 |

    Details

    8 | 9 |
    10 |

    Job

    11 |
    12 |
    13 |
    14 | @Html.DisplayNameFor(model => model.AgentId) 15 |
    16 | 17 |
    18 | @Html.DisplayFor(model => model.AgentId) 19 |
    20 | 21 |
    22 | @Html.DisplayNameFor(model => model.JobNumber) 23 |
    24 | 25 |
    26 | @Html.DisplayFor(model => model.JobNumber) 27 |
    28 | 29 |
    30 | @Html.DisplayNameFor(model => model.Title) 31 |
    32 | 33 |
    34 | @Html.DisplayFor(model => model.Title) 35 |
    36 | 37 |
    38 | @Html.DisplayNameFor(model => model.StartTime) 39 |
    40 | 41 |
    42 | @Html.DisplayFor(model => model.StartTime) 43 |
    44 | 45 |
    46 | @Html.DisplayNameFor(model => model.EndTime) 47 |
    48 | 49 |
    50 | @Html.DisplayFor(model => model.EndTime) 51 |
    52 | 53 |
    54 | @Html.DisplayNameFor(model => model.Status) 55 |
    56 | 57 |
    58 | @Html.DisplayFor(model => model.Status) 59 |
    60 | 61 |
    62 | @Html.DisplayNameFor(model => model.CustomerName) 63 |
    64 | 65 |
    66 | @Html.DisplayFor(model => model.CustomerName) 67 |
    68 | 69 |
    70 | @Html.DisplayNameFor(model => model.CustomerAddress) 71 |
    72 | 73 |
    74 | @Html.DisplayFor(model => model.CustomerAddress) 75 |
    76 | 77 |
    78 | @Html.DisplayNameFor(model => model.CustomerPhoneNumber) 79 |
    80 | 81 |
    82 | @Html.DisplayFor(model => model.CustomerPhoneNumber) 83 |
    84 | 85 |
    86 | @Html.DisplayNameFor(model => model.WorkPerformed) 87 |
    88 | 89 |
    90 | @Html.DisplayFor(model => model.WorkPerformed) 91 |
    92 | 93 |
    94 | @Html.DisplayNameFor(model => model.Version) 95 |
    96 | 97 |
    98 | @Html.DisplayFor(model => model.Version) 99 |
    100 | 101 |
    102 | @Html.DisplayNameFor(model => model.CreatedAt) 103 |
    104 | 105 |
    106 | @Html.DisplayFor(model => model.CreatedAt) 107 |
    108 | 109 |
    110 | @Html.DisplayNameFor(model => model.UpdatedAt) 111 |
    112 | 113 |
    114 | @Html.DisplayFor(model => model.UpdatedAt) 115 |
    116 | 117 |
    118 | @Html.DisplayNameFor(model => model.Deleted) 119 |
    120 | 121 |
    122 | @Html.DisplayFor(model => model.Deleted) 123 |
    124 | 125 |
    126 |
    127 |

    128 | @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) | 129 | @Html.ActionLink("Back to List", "Index") 130 |

    131 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Admin/Edit.cshtml: -------------------------------------------------------------------------------- 1 | @model FieldEngineerLiteService.DataObjects.Job 2 | 3 | @{ 4 | ViewBag.Title = "Edit"; 5 | } 6 | 7 |

    Edit

    8 | 9 | 10 | @using (Html.BeginForm()) 11 | { 12 | @Html.AntiForgeryToken() 13 | 14 |
    15 |

    Job

    16 |
    17 | @Html.ValidationSummary(true, "", new { @class = "text-danger" }) 18 | @Html.HiddenFor(model => model.Id) 19 | 20 |
    21 | @Html.LabelFor(model => model.AgentId, htmlAttributes: new { @class = "control-label col-md-2" }) 22 |
    23 | @Html.EditorFor(model => model.AgentId, new { htmlAttributes = new { @class = "form-control" } }) 24 | @Html.ValidationMessageFor(model => model.AgentId, "", new { @class = "text-danger" }) 25 |
    26 |
    27 | 28 |
    29 | @Html.LabelFor(model => model.JobNumber, htmlAttributes: new { @class = "control-label col-md-2" }) 30 |
    31 | @Html.EditorFor(model => model.JobNumber, new { htmlAttributes = new { @class = "form-control" } }) 32 | @Html.ValidationMessageFor(model => model.JobNumber, "", new { @class = "text-danger" }) 33 |
    34 |
    35 | 36 |
    37 | @Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" }) 38 |
    39 | @Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } }) 40 | @Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" }) 41 |
    42 |
    43 | 44 |
    45 | @Html.LabelFor(model => model.StartTime, htmlAttributes: new { @class = "control-label col-md-2" }) 46 |
    47 | @Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { @class = "form-control" } }) 48 | @Html.ValidationMessageFor(model => model.StartTime, "", new { @class = "text-danger" }) 49 |
    50 |
    51 | 52 |
    53 | @Html.LabelFor(model => model.EndTime, htmlAttributes: new { @class = "control-label col-md-2" }) 54 |
    55 | @Html.EditorFor(model => model.EndTime, new { htmlAttributes = new { @class = "form-control" } }) 56 | @Html.ValidationMessageFor(model => model.EndTime, "", new { @class = "text-danger" }) 57 |
    58 |
    59 | 60 |
    61 | @Html.LabelFor(model => model.Status, htmlAttributes: new { @class = "control-label col-md-2" }) 62 |
    63 | @Html.EditorFor(model => model.Status, new { htmlAttributes = new { @class = "form-control" } }) 64 | @Html.ValidationMessageFor(model => model.Status, "", new { @class = "text-danger" }) 65 |
    66 |
    67 | 68 |
    69 | @Html.LabelFor(model => model.CustomerName, htmlAttributes: new { @class = "control-label col-md-2" }) 70 |
    71 | @Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { @class = "form-control" } }) 72 | @Html.ValidationMessageFor(model => model.CustomerName, "", new { @class = "text-danger" }) 73 |
    74 |
    75 | 76 |
    77 | @Html.LabelFor(model => model.CustomerAddress, htmlAttributes: new { @class = "control-label col-md-2" }) 78 |
    79 | @Html.EditorFor(model => model.CustomerAddress, new { htmlAttributes = new { @class = "form-control" } }) 80 | @Html.ValidationMessageFor(model => model.CustomerAddress, "", new { @class = "text-danger" }) 81 |
    82 |
    83 | 84 |
    85 | @Html.LabelFor(model => model.CustomerPhoneNumber, htmlAttributes: new { @class = "control-label col-md-2" }) 86 |
    87 | @Html.EditorFor(model => model.CustomerPhoneNumber, new { htmlAttributes = new { @class = "form-control" } }) 88 | @Html.ValidationMessageFor(model => model.CustomerPhoneNumber, "", new { @class = "text-danger" }) 89 |
    90 |
    91 | 92 |
    93 | @Html.LabelFor(model => model.WorkPerformed, htmlAttributes: new { @class = "control-label col-md-2" }) 94 |
    95 | @Html.EditorFor(model => model.WorkPerformed, new { htmlAttributes = new { @class = "form-control" } }) 96 | @Html.ValidationMessageFor(model => model.WorkPerformed, "", new { @class = "text-danger" }) 97 |
    98 |
    99 | 100 |
    101 | @Html.LabelFor(model => model.CreatedAt, htmlAttributes: new { @class = "control-label col-md-2" }) 102 |
    103 | @Html.EditorFor(model => model.CreatedAt, new { htmlAttributes = new { @class = "form-control" } }) 104 | @Html.ValidationMessageFor(model => model.CreatedAt, "", new { @class = "text-danger" }) 105 |
    106 |
    107 | 108 |
    109 | @Html.LabelFor(model => model.UpdatedAt, htmlAttributes: new { @class = "control-label col-md-2" }) 110 |
    111 | @Html.EditorFor(model => model.UpdatedAt, new { htmlAttributes = new { @class = "form-control" } }) 112 | @Html.ValidationMessageFor(model => model.UpdatedAt, "", new { @class = "text-danger" }) 113 |
    114 | @Html.HiddenFor(model => model.Version) 115 |
    116 | 117 |
    118 | @Html.LabelFor(model => model.Deleted, htmlAttributes: new { @class = "control-label col-md-2" }) 119 |
    120 |
    121 | @Html.EditorFor(model => model.Deleted) 122 | @Html.ValidationMessageFor(model => model.Deleted, "", new { @class = "text-danger" }) 123 |
    124 |
    125 |
    126 | 127 |
    128 |
    129 | 130 |
    131 |
    132 |
    133 | } 134 | 135 |
    136 | @Html.ActionLink("Back to List", "Index") 137 |
    138 | 139 | @section Scripts { 140 | @Scripts.Render("~/bundles/jqueryval") 141 | } 142 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Admin/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | 3 | @{ 4 | ViewBag.Title = "Index"; 5 | } 6 | 7 |

    Tasks for my agents

    8 | 9 |

    10 | @Html.ActionLink("Create New", "Create") 11 |

    12 | 13 | 14 | 17 | 20 | 23 | 26 | 29 | 32 | 33 | 34 | 35 | 36 | @foreach (var item in Model) { 37 | 38 | 41 | 42 | 45 | 48 | 51 | 54 | 57 | 58 | 63 | 64 | } 65 | 66 |
    15 | Agent 16 | 18 | @Html.DisplayNameFor(model => model.Title) 19 | 21 | @Html.DisplayNameFor(model => model.Status) 22 | 24 | @Html.DisplayNameFor(model => model.CustomerName) 25 | 27 | @Html.DisplayNameFor(model => model.CustomerAddress) 28 | 30 | @Html.DisplayNameFor(model => model.CustomerPhoneNumber) 31 |
    39 | @Html.DisplayFor(modelItem => item.AgentId) 40 | 43 | @Html.DisplayFor(modelItem => item.Title) 44 | 46 | @Html.DisplayFor(modelItem => item.Status) 47 | 49 | @Html.DisplayFor(modelItem => item.CustomerName) 50 | 52 | @Html.DisplayFor(modelItem => item.CustomerAddress) 53 | 55 | @Html.DisplayFor(modelItem => item.CustomerPhoneNumber) 56 | 59 | @Html.ActionLink("Edit", "Edit", new { id=item.Id }) | 60 | @Html.ActionLink("Details", "Details", new { id=item.Id }) | 61 | @Html.ActionLink("Delete", "Delete", new { id=item.Id }) 62 |
    67 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | @{ 3 | Layout = null; 4 | } 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 |
    14 |

    Error.

    15 |

    An error occurred while processing your request.

    16 |
    17 | 18 | 19 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - My ASP.NET Application 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 27 | 28 |
    29 | @RenderBody() 30 |
    31 |
    32 |

    © @DateTime.Now.Year - Fabrikam

    33 |
    34 |
    35 | 36 | @Scripts.Render("~/bundles/jquery") 37 | @Scripts.Render("~/bundles/bootstrap") 38 | @RenderSection("scripts", required: false) 39 | 40 | -------------------------------------------------------------------------------- /server/FieldEngineer/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /server/FieldEngineer/Views/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /server/FieldEngineer/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /server/FieldEngineer/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /server/FieldEngineer/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 |
    9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Vy8CgQgbu3qH5JHTK0op4kR8114= QTJu3Gttpt8hhCktGelNeXj4Yp8= 1ruqF7/L+m1tqnJVscaOtNRNHIE= 6 | -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/Microsoft.VC90.CRT/README_ENU.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/Microsoft.VC90.CRT/README_ENU.txt -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/Microsoft.VC90.CRT/msvcr90.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/Microsoft.VC90.CRT/msvcr90.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceca40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceca40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlcecompact40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlcecompact40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceer40EN.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceer40EN.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceme40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceme40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceqp40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlceqp40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlcese40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/amd64/sqlcese40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/Microsoft.VC90.CRT/Microsoft.VC90.CRT.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | +CXED+6HzJlSphyMNOn27ujadC0= MyKED+9DyS+1XcMeaC0Zlw2vFZ0= EeyDE7og6WoPd2oBhYbMEnpFHhY= 6 | -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/Microsoft.VC90.CRT/README_ENU.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/Microsoft.VC90.CRT/README_ENU.txt -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/Microsoft.VC90.CRT/msvcr90.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/Microsoft.VC90.CRT/msvcr90.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceca40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceca40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlcecompact40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlcecompact40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceer40EN.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceer40EN.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceme40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceme40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceqp40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlceqp40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/_bin_deployableAssemblies/x86/sqlcese40.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/_bin_deployableAssemblies/x86/sqlcese40.dll -------------------------------------------------------------------------------- /server/FieldEngineer/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /server/FieldEngineer/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /server/FieldEngineer/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/app-service-mobile-dotnet-fieldengineer/0cfb3b345acafc7a5ef72480d17dc21387a4b91b/server/FieldEngineer/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /server/FieldEngineer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | --------------------------------------------------------------------------------