├── .DS_Store
├── .angular-cli.json
├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── ClientApp
├── .DS_Store
├── app
│ ├── app.config.ts
│ ├── app.error-handler.ts
│ ├── app.module.ts
│ ├── components
│ │ ├── admin
│ │ │ └── admin.component.ts
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ └── app.component.ts
│ │ ├── counter
│ │ │ ├── counter.component.html
│ │ │ └── counter.component.ts
│ │ ├── fetchdata
│ │ │ ├── fetchdata.component.html
│ │ │ └── fetchdata.component.ts
│ │ ├── home
│ │ │ ├── home.component.html
│ │ │ └── home.component.ts
│ │ ├── navmenu
│ │ │ ├── navmenu.component.css
│ │ │ ├── navmenu.component.html
│ │ │ └── navmenu.component.ts
│ │ ├── shared
│ │ │ └── pagination.component.ts
│ │ ├── vehicle-form
│ │ │ ├── vehicle-form.component.css
│ │ │ ├── vehicle-form.component.html
│ │ │ └── vehicle-form.component.ts
│ │ ├── vehicle-list
│ │ │ ├── vehicle-list.html
│ │ │ └── vehicle-list.ts
│ │ └── view-vehicle
│ │ │ ├── view-vehicle.html
│ │ │ └── view-vehicle.ts
│ ├── models
│ │ └── vehicle.ts
│ └── services
│ │ ├── admin-auth-guard.service.ts
│ │ ├── auth-gaurd.service.ts
│ │ ├── auth.service.ts
│ │ ├── photo.service.ts
│ │ ├── progress.service.ts
│ │ └── vehicle.service.ts
├── boot-client.ts
└── boot-server.ts
├── Controllers
├── .DS_Store
├── AppPolicies.cs
├── FeaturesController.cs
├── HomeController.cs
├── MakesController.cs
├── PhotosController.cs
├── Resources
│ ├── ContactResource.cs
│ ├── KeyValuePairResource.cs
│ ├── MakeResource.cs
│ ├── PhotoResource.cs
│ ├── QueryResultResource.cs
│ ├── SaveVehicleResource.cs
│ ├── VehicleQueryResource.cs
│ └── VehicleResource.cs
├── SampleDataController.cs
└── VehiclesController.cs
├── Core
├── FileSystemPhotoStorage.cs
├── IPhotoRepository.cs
├── IPhotoService.cs
├── IPhotoStorage.cs
├── IUnitOfWork.cs
├── IVehicleRepository.cs
├── Models
│ ├── Feature.cs
│ ├── Make.cs
│ ├── Model.cs
│ ├── Photo.cs
│ ├── PhotoSettings.cs
│ ├── QueryResult.cs
│ ├── Vehicle.cs
│ ├── VehicleFeature.cs
│ └── VehicleQuery.cs
└── PhotoService.cs
├── Extensions
├── IQueryObject.cs
└── IQueryableExtensions.cs
├── Mapping
└── MappingProfile.cs
├── Migrations
├── 20170320232928_InitialModel.Designer.cs
├── 20170320232928_InitialModel.cs
├── 20170321005120_ApplyConstraints.Designer.cs
├── 20170321005120_ApplyConstraints.cs
├── 20170321005742_SeedDatabase.Designer.cs
├── 20170321005742_SeedDatabase.cs
├── 20170322022552_AddFeature.Designer.cs
├── 20170322022552_AddFeature.cs
├── 20170322022625_SeedFeatures.Designer.cs
├── 20170322022625_SeedFeatures.cs
├── 20170326223931_AddVehicle.Designer.cs
├── 20170326223931_AddVehicle.cs
├── 20170412005215_AddPhoto.Designer.cs
├── 20170412005215_AddPhoto.cs
└── VegaDbContextModelSnapshot.cs
├── Persistence
├── PhotoRepository.cs
├── UnitOfWork.cs
├── VegaDbContext.cs
└── VehicleRepository.cs
├── Program.cs
├── README.md
├── Startup.cs
├── Vega.csproj
├── Views
├── Home
│ └── Index.cshtml
├── Shared
│ ├── Error.cshtml
│ └── _Layout.cshtml
├── _ViewImports.cshtml
└── _ViewStart.cshtml
├── appsettings.json
├── global.json
├── package.json
├── tsconfig.json
├── web.config
├── webpack.config.js
├── webpack.config.vendor.js
└── wwwroot
├── .DS_Store
└── favicon.ico
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/.DS_Store
--------------------------------------------------------------------------------
/.angular-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "project": {
4 | "name": "hello-world"
5 | },
6 | "apps": [
7 | {
8 | "root": "ClientApp",
9 | "outDir": "dist",
10 | "assets": [
11 | "assets",
12 | "favicon.ico"
13 | ],
14 | "index": "index.html",
15 | "main": "main.ts",
16 | "polyfills": "polyfills.ts",
17 | "test": "test.ts",
18 | "tsconfig": "tsconfig.app.json",
19 | "testTsconfig": "tsconfig.spec.json",
20 | "prefix": "app",
21 | "styles": [
22 | "styles.css"
23 | ],
24 | "scripts": [],
25 | "environmentSource": "environments/environment.ts",
26 | "environments": {
27 | "dev": "environments/environment.ts",
28 | "prod": "environments/environment.prod.ts"
29 | }
30 | }
31 | ],
32 | "e2e": {
33 | "protractor": {
34 | "config": "./protractor.conf.js"
35 | }
36 | },
37 | "lint": [
38 | {
39 | "project": "src/tsconfig.app.json"
40 | },
41 | {
42 | "project": "src/tsconfig.spec.json"
43 | },
44 | {
45 | "project": "e2e/tsconfig.e2e.json"
46 | }
47 | ],
48 | "test": {
49 | "karma": {
50 | "config": "./karma.conf.js"
51 | }
52 | },
53 | "defaults": {
54 | "styleExt": "css",
55 | "component": {}
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /Properties/launchSettings.json
2 |
3 | ## Ignore Visual Studio temporary files, build results, and
4 | ## files generated by popular Visual Studio add-ons.
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | build/
23 | bld/
24 | bin/
25 | Bin/
26 | obj/
27 | Obj/
28 |
29 | # Visual Studio 2015 cache/options directory
30 | .vs/
31 | /wwwroot/uploads/**
32 | /wwwroot/dist/**
33 | /ClientApp/dist/**
34 |
35 | # Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
36 | !/wwwroot/dist/_placeholder.txt
37 | !/ClientApp/dist/_placeholder.txt
38 |
39 |
40 | # MSTest test Results
41 | [Tt]est[Rr]esult*/
42 | [Bb]uild[Ll]og.*
43 |
44 | # NUNIT
45 | *.VisualState.xml
46 | TestResult.xml
47 |
48 | # Build Results of an ATL Project
49 | [Dd]ebugPS/
50 | [Rr]eleasePS/
51 | dlldata.c
52 |
53 | # DNX
54 | project.lock.json
55 | artifacts/
56 |
57 | *_i.c
58 | *_p.c
59 | *_i.h
60 | *.ilk
61 | *.meta
62 | *.obj
63 | *.pch
64 | *.pdb
65 | *.pgc
66 | *.pgd
67 | *.rsp
68 | *.sbr
69 | *.tlb
70 | *.tli
71 | *.tlh
72 | *.tmp
73 | *.tmp_proj
74 | *.log
75 | *.vspscc
76 | *.vssscc
77 | .builds
78 | *.pidb
79 | *.svclog
80 | *.scc
81 |
82 | # Chutzpah Test files
83 | _Chutzpah*
84 |
85 | # Visual C++ cache files
86 | ipch/
87 | *.aps
88 | *.ncb
89 | *.opendb
90 | *.opensdf
91 | *.sdf
92 | *.cachefile
93 |
94 | # Visual Studio profiler
95 | *.psess
96 | *.vsp
97 | *.vspx
98 | *.sap
99 |
100 | # TFS 2012 Local Workspace
101 | $tf/
102 |
103 | # Guidance Automation Toolkit
104 | *.gpState
105 |
106 | # ReSharper is a .NET coding add-in
107 | _ReSharper*/
108 | *.[Rr]e[Ss]harper
109 | *.DotSettings.user
110 |
111 | # JustCode is a .NET coding add-in
112 | .JustCode
113 |
114 | # TeamCity is a build add-in
115 | _TeamCity*
116 |
117 | # DotCover is a Code Coverage Tool
118 | *.dotCover
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # NuGet Packages
157 | *.nupkg
158 | # The packages folder can be ignored because of Package Restore
159 | **/packages/*
160 | # except build/, which is used as an MSBuild target.
161 | !**/packages/build/
162 | # Uncomment if necessary however generally it will be regenerated when needed
163 | #!**/packages/repositories.config
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Microsoft Azure ApplicationInsights config file
174 | ApplicationInsights.config
175 |
176 | # Windows Store app package directory
177 | AppPackages/
178 | BundleArtifacts/
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.pfx
193 | *.publishsettings
194 | orleans.codegen.cs
195 |
196 | # Workaround for https://github.com/aspnet/JavaScriptServices/issues/235
197 | /node_modules/**
198 | !/node_modules/_placeholder.txt
199 |
200 | /yarn.lock
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 |
248 | # FAKE - F# Make
249 | .fake/
250 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": ".NET Core Launch (web)",
6 | "type": "coreclr",
7 | "request": "launch",
8 | "preLaunchTask": "build",
9 | "program": "${workspaceRoot}/bin/Debug/netcoreapp1.1/Vega.dll",
10 | "args": [],
11 | "cwd": "${workspaceRoot}",
12 | "stopAtEntry": false,
13 | "internalConsoleOptions": "openOnSessionStart",
14 | "launchBrowser": {
15 | "enabled": true,
16 | "args": "${auto-detect-url}",
17 | "windows": {
18 | "command": "cmd.exe",
19 | "args": "/C start ${auto-detect-url}"
20 | },
21 | "osx": {
22 | "command": "open"
23 | },
24 | "linux": {
25 | "command": "xdg-open"
26 | }
27 | },
28 | "env": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | },
31 | "sourceFileMap": {
32 | "/Views": "${workspaceRoot}/Views"
33 | }
34 | },
35 | {
36 | "name": ".NET Core Attach",
37 | "type": "coreclr",
38 | "request": "attach",
39 | "processId": "${command:pickProcess}"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "command": "dotnet",
4 | "isShellCommand": true,
5 | "args": [],
6 | "tasks": [
7 | {
8 | "taskName": "build",
9 | "args": [
10 | "${workspaceRoot}/Vega.csproj"
11 | ],
12 | "isBuildCommand": true,
13 | "problemMatcher": "$msCompile"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/ClientApp/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/ClientApp/.DS_Store
--------------------------------------------------------------------------------
/ClientApp/app/app.config.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/ClientApp/app/app.config.ts
--------------------------------------------------------------------------------
/ClientApp/app/app.error-handler.ts:
--------------------------------------------------------------------------------
1 | import * as Raven from 'raven-js';
2 | import { ToastyService } from 'ng2-toasty';
3 | import { ErrorHandler, Inject, NgZone, isDevMode } from '@angular/core';
4 |
5 | export class AppErrorHandler implements ErrorHandler {
6 | constructor(
7 | private ngZone: NgZone,
8 | @Inject(ToastyService) private toastyService: ToastyService) {
9 | }
10 |
11 | handleError(error: any): void {
12 | this.ngZone.run(() => {
13 | this.toastyService.error({
14 | title: 'Error',
15 | msg: 'An unexpected error happened.',
16 | theme: 'bootstrap',
17 | showClose: true,
18 | timeout: 5000
19 | });
20 | });
21 |
22 | if (!isDevMode())
23 | Raven.captureException(error.originalError || error);
24 | else
25 | throw error;
26 | }
27 | }
--------------------------------------------------------------------------------
/ClientApp/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { AdminAuthGuard } from './services/admin-auth-guard.service';
2 | import { AuthGuard } from './services/auth-gaurd.service';
3 | import { AdminComponent } from './components/admin/admin.component';
4 | import { Auth } from './services/auth.service';
5 | import { BrowserXhr } from '@angular/http';
6 | import { BrowserXhrWithProgress, ProgressService } from './services/progress.service';
7 | import { ViewVehicleComponent } from './components/view-vehicle/view-vehicle';
8 | import { PaginationComponent } from './components/shared/pagination.component';
9 | import { VehicleListComponent } from './components/vehicle-list/vehicle-list';
10 | import * as Raven from 'raven-js';
11 | import { FormsModule } from '@angular/forms';
12 | import { NgModule, ErrorHandler } from '@angular/core';
13 | import { RouterModule } from '@angular/router';
14 | import { ToastyModule } from 'ng2-toasty';
15 | import { UniversalModule } from 'angular2-universal';
16 | import { ChartModule } from 'angular2-chartjs';
17 |
18 | import { AppComponent } from './components/app/app.component'
19 | import { AppErrorHandler } from './app.error-handler';
20 | import { VehicleService } from './services/vehicle.service';
21 | import { NavMenuComponent } from './components/navmenu/navmenu.component';
22 | import { HomeComponent } from './components/home/home.component';
23 | import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
24 | import { CounterComponent } from './components/counter/counter.component';
25 | import { VehicleFormComponent } from './components/vehicle-form/vehicle-form.component';
26 | import { PhotoService } from "./services/photo.service";
27 | import { AUTH_PROVIDERS } from "angular2-jwt/angular2-jwt";
28 |
29 | Raven.config('https://d37bba0c459b46e0857e6e2b3aeff09b@sentry.io/155312').install();
30 |
31 | @NgModule({
32 | bootstrap: [ AppComponent ],
33 | declarations: [
34 | AppComponent,
35 | NavMenuComponent,
36 | CounterComponent,
37 | FetchDataComponent,
38 | HomeComponent,
39 | VehicleFormComponent,
40 | VehicleListComponent,
41 | ViewVehicleComponent,
42 | PaginationComponent,
43 | AdminComponent
44 | ],
45 | imports: [
46 | UniversalModule, // Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
47 | FormsModule,
48 | ToastyModule.forRoot(),
49 | ChartModule,
50 | RouterModule.forRoot([
51 | { path: '', redirectTo: 'vehicles', pathMatch: 'full' },
52 | { path: 'vehicles/new', component: VehicleFormComponent, canActivate: [ AuthGuard ] },
53 | { path: 'vehicles/edit/:id', component: VehicleFormComponent, canActivate: [ AuthGuard ] },
54 | { path: 'vehicles/:id', component: ViewVehicleComponent },
55 | { path: 'vehicles', component: VehicleListComponent },
56 | { path: 'admin', component: AdminComponent, canActivate: [ AdminAuthGuard ] },
57 | { path: 'home', component: HomeComponent },
58 | { path: '**', redirectTo: 'home' }
59 | ])
60 | ],
61 | providers: [
62 | { provide: ErrorHandler, useClass: AppErrorHandler },
63 | Auth,
64 | AuthGuard,
65 | AUTH_PROVIDERS,
66 | AdminAuthGuard,
67 | VehicleService,
68 | PhotoService
69 | ]
70 | })
71 | export class AppModule {
72 | }
73 |
--------------------------------------------------------------------------------
/ClientApp/app/components/admin/admin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | template: `
Admin
5 |
6 | `
7 | })
8 |
9 | export class AdminComponent implements OnInit {
10 | data = {
11 | labels: ['BMW', 'Audi', 'Mazda'],
12 | datasets: [
13 | {
14 | data: [5, 3, 1],
15 | backgroundColor: [
16 | "#ff6384",
17 | "#36a2eb",
18 | "#ffce56"
19 | ]
20 | }
21 | ]
22 | };
23 |
24 | constructor() { }
25 |
26 | ngOnInit() { }
27 | }
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 767px) {
2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
3 | .body-content {
4 | padding-top: 50px;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ClientApp/app/components/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app',
5 | templateUrl: './app.component.html',
6 | styleUrls: ['./app.component.css']
7 | })
8 | export class AppComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/ClientApp/app/components/counter/counter.component.html:
--------------------------------------------------------------------------------
1 | Counter
2 |
3 | This is a simple example of an Angular 2 component.
4 |
5 | Current count: {{ currentCount }}
6 |
7 | Increment
8 |
--------------------------------------------------------------------------------
/ClientApp/app/components/counter/counter.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'counter',
5 | templateUrl: './counter.component.html'
6 | })
7 | export class CounterComponent {
8 | public currentCount = 0;
9 |
10 | public incrementCounter() {
11 | this.currentCount++;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ClientApp/app/components/fetchdata/fetchdata.component.html:
--------------------------------------------------------------------------------
1 | Weather forecast 2
2 |
3 | This component demonstrates fetching data from the server.
4 |
5 | Loading...
6 |
7 |
8 |
9 |
10 | Date
11 | Temp. (C)
12 | Temp. (F)
13 | Summary
14 |
15 |
16 |
17 |
18 | {{ forecast.dateFormatted }}
19 | {{ forecast.temperatureC }}
20 | {{ forecast.temperatureF }}
21 | {{ forecast.summary }}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ClientApp/app/components/fetchdata/fetchdata.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Http } from '@angular/http';
3 |
4 | @Component({
5 | selector: 'fetchdata',
6 | templateUrl: './fetchdata.component.html'
7 | })
8 | export class FetchDataComponent {
9 | public forecasts: WeatherForecast[];
10 |
11 | constructor(http: Http) {
12 | http.get('/api/SampleData/WeatherForecasts').subscribe(result => {
13 | this.forecasts = result.json() as WeatherForecast[];
14 | });
15 | }
16 | }
17 |
18 | interface WeatherForecast {
19 | dateFormatted: string;
20 | temperatureC: number;
21 | temperatureF: number;
22 | summary: string;
23 | }
24 |
--------------------------------------------------------------------------------
/ClientApp/app/components/home/home.component.html:
--------------------------------------------------------------------------------
1 | Hello, world!
2 | Welcome to your new single-page application, built with:
3 |
9 | To help you get started, we've also set up:
10 |
11 | Client-side navigation . For example, click Counter then Back to return here.
12 | Server-side prerendering . For faster initial loading and improved SEO, your Angular 2 app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.
13 | Webpack dev middleware . In development mode, there's no need to run the webpack
build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.
14 | Hot module replacement . In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, your Angular 2 app will be rebuilt and a new instance injected is into the page.
15 | Efficient production builds . In production mode, development-time features are disabled, and the webpack
build tool produces minified static CSS and JavaScript files.
16 |
17 |
--------------------------------------------------------------------------------
/ClientApp/app/components/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'home',
5 | templateUrl: './home.component.html'
6 | })
7 | export class HomeComponent {
8 | }
9 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.css:
--------------------------------------------------------------------------------
1 | li .glyphicon {
2 | margin-right: 10px;
3 | }
4 |
5 | /* Highlighting rules for nav menu items */
6 | li.link-active a,
7 | li.link-active a:hover,
8 | li.link-active a:focus {
9 | background-color: #4189C7;
10 | color: white;
11 | }
12 |
13 | /* Keep the nav menu independent of scrolling and on top of other items */
14 | .main-nav {
15 | position: fixed;
16 | top: 0;
17 | left: 0;
18 | right: 0;
19 | z-index: 1;
20 | }
21 |
22 | @media (min-width: 768px) {
23 | /* On small screens, convert the nav menu to a vertical sidebar */
24 | .main-nav {
25 | height: 100%;
26 | width: calc(25% - 20px);
27 | }
28 | .navbar {
29 | border-radius: 0px;
30 | border-width: 0px;
31 | height: 100%;
32 | }
33 | .navbar-header {
34 | float: none;
35 | }
36 | .navbar-collapse {
37 | border-top: 1px solid #444;
38 | padding: 0px;
39 | }
40 | .navbar ul {
41 | float: none;
42 | }
43 | .navbar li {
44 | float: none;
45 | font-size: 15px;
46 | margin: 6px;
47 | }
48 | .navbar li a {
49 | padding: 10px 16px;
50 | border-radius: 4px;
51 | }
52 | .navbar a {
53 | /* If a menu item's text is too long, truncate it */
54 | width: 100%;
55 | white-space: nowrap;
56 | overflow: hidden;
57 | text-overflow: ellipsis;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.html:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/ClientApp/app/components/navmenu/navmenu.component.ts:
--------------------------------------------------------------------------------
1 | import { Auth } from './../../services/auth.service';
2 | import { Component } from '@angular/core';
3 |
4 | @Component({
5 | selector: 'nav-menu',
6 | templateUrl: './navmenu.component.html',
7 | styleUrls: ['./navmenu.component.css']
8 | })
9 | export class NavMenuComponent {
10 | constructor(private auth: Auth) {}
11 | }
12 |
--------------------------------------------------------------------------------
/ClientApp/app/components/shared/pagination.component.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Component,
3 | Input,
4 | Output,
5 | EventEmitter } from '@angular/core';
6 | import { OnChanges } from '@angular/core';
7 |
8 | @Component({
9 | selector: 'pagination',
10 | template: `
11 | pageSize">
12 |
27 |
28 | `
29 | })
30 | export class PaginationComponent implements OnChanges {
31 | @Input('total-items') totalItems;
32 | @Input('page-size') pageSize = 10;
33 | @Output('page-changed') pageChanged = new EventEmitter();
34 | pages: any[];
35 | currentPage = 1;
36 |
37 | ngOnChanges(){
38 | this.currentPage = 1;
39 |
40 | var pagesCount = Math.ceil(this.totalItems / this.pageSize);
41 | this.pages = [];
42 | for (var i = 1; i <= pagesCount; i++)
43 | this.pages.push(i);
44 | }
45 |
46 | changePage(page){
47 | this.currentPage = page;
48 | this.pageChanged.emit(page);
49 | }
50 |
51 | previous(){
52 | if (this.currentPage == 1)
53 | return;
54 |
55 | this.currentPage--;
56 | this.pageChanged.emit(this.currentPage);
57 | }
58 |
59 | next(){
60 | if (this.currentPage == this.pages.length)
61 | return;
62 |
63 | this.currentPage++;
64 | this.pageChanged.emit(this.currentPage);
65 | }
66 | }
--------------------------------------------------------------------------------
/ClientApp/app/components/vehicle-form/vehicle-form.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/ClientApp/app/components/vehicle-form/vehicle-form.component.css
--------------------------------------------------------------------------------
/ClientApp/app/components/vehicle-form/vehicle-form.component.html:
--------------------------------------------------------------------------------
1 | New Vehicle
2 |
3 | {{ vehicle | json }}
4 |
5 |
--------------------------------------------------------------------------------
/ClientApp/app/components/vehicle-form/vehicle-form.component.ts:
--------------------------------------------------------------------------------
1 | import * as _ from 'underscore';
2 | import { SaveVehicle, Vehicle } from './../../models/vehicle';
3 | import { Observable } from 'rxjs/Observable';
4 | import { ActivatedRoute, Router } from '@angular/router';
5 | import { VehicleService } from './../../services/vehicle.service';
6 | import { Component, OnInit } from '@angular/core';
7 | import { ToastyService } from "ng2-toasty";
8 | import 'rxjs/add/Observable/forkJoin';
9 |
10 | @Component({
11 | selector: 'app-vehicle-form',
12 | templateUrl: './vehicle-form.component.html',
13 | styleUrls: ['./vehicle-form.component.css']
14 | })
15 | export class VehicleFormComponent implements OnInit {
16 | makes: any[];
17 | models: any[];
18 | features: any[];
19 | vehicle: SaveVehicle = {
20 | id: 0,
21 | makeId: 0,
22 | modelId: 0,
23 | isRegistered: false,
24 | features: [],
25 | contact: {
26 | name: '',
27 | email: '',
28 | phone: '',
29 | }
30 | };
31 |
32 | constructor(
33 | private route: ActivatedRoute,
34 | private router: Router,
35 | private vehicleService: VehicleService,
36 | private toastyService: ToastyService) {
37 |
38 | route.params.subscribe(p => {
39 | this.vehicle.id = +p['id'] || 0;
40 | });
41 | }
42 |
43 | ngOnInit() {
44 | var sources = [
45 | this.vehicleService.getMakes(),
46 | this.vehicleService.getFeatures(),
47 | ];
48 |
49 | if (this.vehicle.id)
50 | sources.push(this.vehicleService.getVehicle(this.vehicle.id));
51 |
52 | Observable.forkJoin(sources).subscribe(data => {
53 | this.makes = data[0];
54 | this.features = data[1];
55 |
56 | if (this.vehicle.id) {
57 | this.setVehicle(data[2]);
58 | this.populateModels();
59 | }
60 | }, err => {
61 | if (err.status == 404)
62 | this.router.navigate(['/home']);
63 | });
64 | }
65 |
66 | private setVehicle(v: Vehicle) {
67 | this.vehicle.id = v.id;
68 | this.vehicle.makeId = v.make.id;
69 | this.vehicle.modelId = v.model.id;
70 | this.vehicle.isRegistered = v.isRegistered;
71 | this.vehicle.contact = v.contact;
72 | this.vehicle.features = _.pluck(v.features, 'id');
73 | }
74 |
75 | onMakeChange() {
76 | this.populateModels();
77 |
78 | delete this.vehicle.modelId;
79 | }
80 |
81 | private populateModels() {
82 | var selectedMake = this.makes.find(m => m.id == this.vehicle.makeId);
83 | this.models = selectedMake ? selectedMake.models : [];
84 | }
85 |
86 | onFeatureToggle(featureId, $event) {
87 | if ($event.target.checked)
88 | this.vehicle.features.push(featureId);
89 | else {
90 | var index = this.vehicle.features.indexOf(featureId);
91 | this.vehicle.features.splice(index, 1);
92 | }
93 | }
94 |
95 | submit() {
96 | var result$ = (this.vehicle.id) ? this.vehicleService.update(this.vehicle) : this.vehicleService.create(this.vehicle);
97 | result$.subscribe(vehicle => {
98 | this.toastyService.success({
99 | title: 'Success',
100 | msg: 'Data was sucessfully saved.',
101 | theme: 'bootstrap',
102 | showClose: true,
103 | timeout: 5000
104 | });
105 | this.router.navigate(['/vehicles/', vehicle.id])
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/ClientApp/app/components/vehicle-list/vehicle-list.html:
--------------------------------------------------------------------------------
1 | Vehicles
2 |
3 | New Vehicle
4 |
5 |
6 |
7 | Make
8 |
9 |
10 | {{ m.name }}
11 |
12 |
13 |
Reset
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ c.title }}
21 |
26 |
27 |
28 | {{ c.title }}
29 |
30 |
31 |
32 |
33 |
34 |
35 | {{ v.id }}
36 | {{ v.make.name }}
37 | {{ v.model.name }}
38 | {{ v.contact.name }}
39 |
40 | View
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/ClientApp/app/components/vehicle-list/vehicle-list.ts:
--------------------------------------------------------------------------------
1 | import { Auth } from './../../services/auth.service';
2 | import { Vehicle, KeyValuePair } from './../../models/vehicle';
3 | import { VehicleService } from './../../services/vehicle.service';
4 | import { Component, OnInit } from '@angular/core';
5 |
6 | @Component({
7 | templateUrl: 'vehicle-list.html'
8 | })
9 | export class VehicleListComponent implements OnInit {
10 | private readonly PAGE_SIZE = 3;
11 |
12 | queryResult: any = {};
13 | makes: KeyValuePair[];
14 | query: any = {
15 | pageSize: this.PAGE_SIZE
16 | };
17 | columns = [
18 | { title: 'Id' },
19 | { title: 'Contact Name', key: 'contactName', isSortable: true },
20 | { title: 'Make', key: 'make', isSortable: true },
21 | { title: 'Model', key: 'model', isSortable: true },
22 | { }
23 | ];
24 |
25 | constructor(private vehicleService: VehicleService, private auth: Auth) { }
26 |
27 | ngOnInit() {
28 | this.vehicleService.getMakes()
29 | .subscribe(makes => this.makes = makes);
30 |
31 | this.populateVehicles();
32 | }
33 |
34 | private populateVehicles() {
35 | this.vehicleService.getVehicles(this.query)
36 | .subscribe(result => this.queryResult = result);
37 | }
38 |
39 | onFilterChange() {
40 | this.query.page = 1;
41 | this.populateVehicles();
42 | }
43 |
44 | resetFilter() {
45 | this.query = {
46 | page: 1,
47 | pageSize: this.PAGE_SIZE
48 | };
49 | this.populateVehicles();
50 | }
51 |
52 | sortBy(columnName) {
53 | if (this.query.sortBy === columnName) {
54 | this.query.isSortAscending = !this.query.isSortAscending;
55 | } else {
56 | this.query.sortBy = columnName;
57 | this.query.isSortAscending = true;
58 | }
59 | this.populateVehicles();
60 | }
61 |
62 | onPageChange(page) {
63 | this.query.page = page;
64 | this.populateVehicles();
65 | }
66 | }
--------------------------------------------------------------------------------
/ClientApp/app/components/view-vehicle/view-vehicle.html:
--------------------------------------------------------------------------------
1 | Vehicle
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
Basics
15 |
16 | Make: {{ vehicle.make.name }}
17 | Model: {{ vehicle.model.name }}
18 | Registered: {{ vehicle.isRegistered ? 'Yes' : 'No' }}
19 |
20 |
Features
21 |
24 |
Contact
25 |
26 | Contact Name: {{ vehicle.contact.name }}
27 | Contact Phone: {{ vehicle.contact.phone }}
28 | Contact Email: {{ vehicle.contact.email }}
29 |
30 |
31 |
32 | Edit
33 | Delete
34 | View All Vehicles
35 |
36 |
37 |
38 |
39 |
Photos
40 |
41 |
42 |
43 | {{ progress.percentage }}% Complete
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/ClientApp/app/components/view-vehicle/view-vehicle.ts:
--------------------------------------------------------------------------------
1 | import { Auth } from './../../services/auth.service';
2 | import { BrowserXhr } from '@angular/http';
3 | import { ProgressService, BrowserXhrWithProgress } from './../../services/progress.service';
4 | import { PhotoService } from './../../services/photo.service';
5 | import { ToastyService } from 'ng2-toasty';
6 | import { VehicleService } from './../../services/vehicle.service';
7 | import { Component, OnInit, ElementRef, ViewChild, NgZone } from '@angular/core';
8 | import { ActivatedRoute, Router } from '@angular/router';
9 |
10 | @Component({
11 | templateUrl: 'view-vehicle.html',
12 | providers: [
13 | { provide: BrowserXhr, useClass: BrowserXhrWithProgress },
14 | ProgressService
15 | ]
16 | })
17 | export class ViewVehicleComponent implements OnInit {
18 | @ViewChild('fileInput') fileInput: ElementRef;
19 | vehicle: any;
20 | vehicleId: number;
21 | photos: any[];
22 | progress: any;
23 |
24 | constructor(
25 | private auth: Auth,
26 | private zone: NgZone,
27 | private route: ActivatedRoute,
28 | private router: Router,
29 | private toasty: ToastyService,
30 | private progressService: ProgressService,
31 | private photoService: PhotoService,
32 | private vehicleService: VehicleService) {
33 |
34 | route.params.subscribe(p => {
35 | this.vehicleId = +p['id'];
36 | if (isNaN(this.vehicleId) || this.vehicleId <= 0) {
37 | router.navigate(['/vehicles']);
38 | return;
39 | }
40 | });
41 | }
42 |
43 | ngOnInit() {
44 | this.photoService.getPhotos(this.vehicleId)
45 | .subscribe(photos => this.photos = photos);
46 |
47 | this.vehicleService.getVehicle(this.vehicleId)
48 | .subscribe(
49 | v => this.vehicle = v,
50 | err => {
51 | if (err.status == 404) {
52 | this.router.navigate(['/vehicles']);
53 | return;
54 | }
55 | });
56 | }
57 |
58 | delete() {
59 | if (confirm("Are you sure?")) {
60 | this.vehicleService.delete(this.vehicle.id)
61 | .subscribe(x => {
62 | this.router.navigate(['/vehicles']);
63 | });
64 | }
65 | }
66 |
67 | uploadPhoto() {
68 | this.progressService.startTracking()
69 | .subscribe(progress => {
70 | this.zone.run(() => {
71 | this.progress = progress;
72 | });
73 | },
74 | null,
75 | () => { this.progress = null; });
76 |
77 | var nativeElement: HTMLInputElement = this.fileInput.nativeElement;
78 | var file = nativeElement.files[0];
79 | nativeElement.value = '';
80 | this.photoService.upload(this.vehicleId, file)
81 | .subscribe(photo => {
82 | this.photos.push(photo);
83 | },
84 | err => {
85 | this.toasty.error({
86 | title: 'Error',
87 | msg: err.text(),
88 | theme: 'bootstrap',
89 | showClose: true,
90 | timeout: 5000
91 | });
92 | });
93 | }
94 | }
--------------------------------------------------------------------------------
/ClientApp/app/models/vehicle.ts:
--------------------------------------------------------------------------------
1 | import { Contact } from './vehicle';
2 |
3 | export interface KeyValuePair {
4 | id: number;
5 | name: string;
6 | }
7 |
8 | export interface Contact {
9 | name: string;
10 | phone: string;
11 | email: string;
12 | }
13 |
14 | export interface Vehicle {
15 | id: number;
16 | model: KeyValuePair;
17 | make: KeyValuePair;
18 | isRegistered: boolean;
19 | features: KeyValuePair[];
20 | contact: Contact;
21 | lastUpdate: string;
22 | }
23 |
24 | export interface SaveVehicle {
25 | id: number;
26 | modelId: number;
27 | makeId: number;
28 | isRegistered: boolean;
29 | features: number[];
30 | contact: Contact;
31 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/admin-auth-guard.service.ts:
--------------------------------------------------------------------------------
1 | import { AuthGuard } from './auth-gaurd.service';
2 | import { CanActivate } from '@angular/router';
3 | import { Auth } from './auth.service';
4 | import { Injectable } from '@angular/core';
5 |
6 | @Injectable()
7 | export class AdminAuthGuard extends AuthGuard {
8 |
9 | constructor(auth: Auth) {
10 | super(auth);
11 | }
12 |
13 | canActivate() {
14 | var isAuthenticated = super.canActivate();
15 |
16 | return isAuthenticated ? this.auth.isInRole('Admin') : false;
17 | }
18 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/auth-gaurd.service.ts:
--------------------------------------------------------------------------------
1 | import { CanActivate } from '@angular/router';
2 | import { Auth } from './auth.service';
3 | import { Injectable } from '@angular/core';
4 |
5 | @Injectable()
6 | export class AuthGuard implements CanActivate {
7 |
8 | constructor(protected auth: Auth) { }
9 |
10 | canActivate() {
11 | if (this.auth.authenticated())
12 | return true;
13 |
14 | window.location.href = 'https://vegaproject.auth0.com/login?client=RfRu3un13aOO73C7X2mH41qxfHRbUc33';
15 | return false;
16 | }
17 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { JwtHelper } from 'angular2-jwt';
2 | // app/auth.service.ts
3 |
4 | import { Injectable } from '@angular/core';
5 | import { tokenNotExpired } from 'angular2-jwt';
6 |
7 | // Avoid name not found warnings
8 | import Auth0Lock from 'auth0-lock';
9 |
10 | @Injectable()
11 | export class Auth {
12 | profile: any;
13 | private roles: string[] = [];
14 |
15 | // Configure Auth0
16 | lock = new Auth0Lock('RfRu3un13aOO73C7X2mH41qxfHRbUc33', 'vegaproject.auth0.com', {});
17 |
18 | constructor() {
19 | this.readUserFromLocalStorage();
20 |
21 | this.lock.on("authenticated", (authResult) => this.onUserAuthenticated(authResult));
22 | }
23 |
24 | private onUserAuthenticated(authResult) {
25 | localStorage.setItem('token', authResult.accessToken);
26 |
27 | this.lock.getUserInfo(authResult.accessToken, (error, profile) => {
28 | if (error)
29 | throw error;
30 |
31 | localStorage.setItem('profile', JSON.stringify(profile));
32 |
33 | this.readUserFromLocalStorage();
34 | });
35 | }
36 |
37 | private readUserFromLocalStorage() {
38 | this.profile = JSON.parse(localStorage.getItem('profile'));
39 |
40 | var token = localStorage.getItem('token');
41 | if (token) {
42 | var jwtHelper = new JwtHelper();
43 | var decodedToken = jwtHelper.decodeToken(token);
44 | this.roles = decodedToken['https://vega.com/roles'] || [];
45 | }
46 | }
47 |
48 | public isInRole(roleName) {
49 | return this.roles.indexOf(roleName) > -1;
50 | }
51 |
52 | public login() {
53 | // Call the show method to display the widget.
54 | this.lock.show();
55 | }
56 |
57 | public authenticated() {
58 | // Check if there's an unexpired JWT
59 | // This searches for an item in localStorage with key == 'token'
60 | return tokenNotExpired('token');
61 | }
62 |
63 | public logout() {
64 | // Remove token from localStorage
65 | localStorage.removeItem('token');
66 | localStorage.removeItem('profile');
67 | this.profile = null;
68 | this.roles = [];
69 | }
70 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/photo.service.ts:
--------------------------------------------------------------------------------
1 | import { Http } from '@angular/http';
2 | import { Injectable } from '@angular/core';
3 |
4 | @Injectable()
5 | export class PhotoService {
6 |
7 | constructor(private http: Http) { }
8 |
9 | upload(vehicleId, photo) {
10 | var formData = new FormData();
11 | formData.append('file', photo);
12 | return this.http.post(`/api/vehicles/${vehicleId}/photos`, formData)
13 | .map(res => res.json());
14 | }
15 |
16 | getPhotos(vehicleId) {
17 | return this.http.get(`/api/vehicles/${vehicleId}/photos`)
18 | .map(res => res.json());
19 | }
20 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/progress.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Subject } from "rxjs/Subject";
3 | import { BrowserXhr } from "@angular/http";
4 |
5 | @Injectable()
6 | export class ProgressService {
7 | private uploadProgress: Subject;
8 |
9 | startTracking() {
10 | this.uploadProgress = new Subject();
11 | return this.uploadProgress;
12 | }
13 |
14 | notify(progress) {
15 | if (this.uploadProgress)
16 | this.uploadProgress.next(progress);
17 | }
18 |
19 | endTracking() {
20 | if (this.uploadProgress)
21 | this.uploadProgress.complete();
22 | }
23 | }
24 |
25 | @Injectable()
26 | export class BrowserXhrWithProgress extends BrowserXhr {
27 |
28 | constructor(private service: ProgressService) { super(); }
29 |
30 | build(): XMLHttpRequest {
31 | var xhr: XMLHttpRequest = super.build();
32 |
33 | xhr.upload.onprogress = (event) => {
34 | this.service.notify(this.createProgress(event));
35 | };
36 |
37 | xhr.upload.onloadend = () => {
38 | this.service.endTracking();
39 | }
40 |
41 | return xhr;
42 | }
43 |
44 | private createProgress(event) {
45 | return {
46 | total: event.total,
47 | percentage: Math.round(event.loaded / event.total * 100)
48 | };
49 | }
50 | }
--------------------------------------------------------------------------------
/ClientApp/app/services/vehicle.service.ts:
--------------------------------------------------------------------------------
1 | import { SaveVehicle } from './../models/vehicle';
2 | import { Injectable } from '@angular/core';
3 | import { Http } from '@angular/http';
4 | import 'rxjs/add/operator/map';
5 | import { AuthHttp } from "angular2-jwt/angular2-jwt";
6 |
7 | @Injectable()
8 | export class VehicleService {
9 | private readonly vehiclesEndpoint = '/api/vehicles';
10 |
11 | constructor(private http: Http, private authHttp: AuthHttp) { }
12 |
13 | getFeatures() {
14 | return this.http.get('/api/features')
15 | .map(res => res.json());
16 | }
17 |
18 | getMakes() {
19 | return this.http.get('/api/makes')
20 | .map(res => res.json());
21 | }
22 |
23 | create(vehicle) {
24 | return this.authHttp.post(this.vehiclesEndpoint, vehicle)
25 | .map(res => res.json());
26 | }
27 |
28 | getVehicle(id) {
29 | return this.http.get(this.vehiclesEndpoint + '/' + id)
30 | .map(res => res.json());
31 | }
32 |
33 | getVehicles(filter) {
34 | return this.http.get(this.vehiclesEndpoint + '?' + this.toQueryString(filter))
35 | .map(res => res.json());
36 | }
37 |
38 | toQueryString(obj) {
39 | var parts = [];
40 | for (var property in obj) {
41 | var value = obj[property];
42 | if (value != null && value != undefined)
43 | parts.push(encodeURIComponent(property) + '=' + encodeURIComponent(value));
44 | }
45 |
46 | return parts.join('&');
47 | }
48 |
49 | update(vehicle: SaveVehicle) {
50 | return this.authHttp.put(this.vehiclesEndpoint + '/' + vehicle.id, vehicle)
51 | .map(res => res.json());
52 | }
53 |
54 | delete(id) {
55 | return this.authHttp.delete(this.vehiclesEndpoint + '/' + id)
56 | .map(res => res.json());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ClientApp/boot-client.ts:
--------------------------------------------------------------------------------
1 | import 'angular2-universal-polyfills/browser';
2 | import { enableProdMode } from '@angular/core';
3 | import { platformUniversalDynamic } from 'angular2-universal';
4 | import { AppModule } from './app/app.module';
5 | import 'bootstrap';
6 | const rootElemTagName = 'app'; // Update this if you change your root component selector
7 |
8 | // Enable either Hot Module Reloading or production mode
9 | if (module['hot']) {
10 | module['hot'].accept();
11 | module['hot'].dispose(() => {
12 | // Before restarting the app, we create a new root element and dispose the old one
13 | const oldRootElem = document.querySelector(rootElemTagName);
14 | const newRootElem = document.createElement(rootElemTagName);
15 | oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem);
16 | platform.destroy();
17 | });
18 | } else {
19 | enableProdMode();
20 | }
21 |
22 | // Boot the application, either now or when the DOM content is loaded
23 | const platform = platformUniversalDynamic();
24 | const bootApplication = () => { platform.bootstrapModule(AppModule); };
25 | if (document.readyState === 'complete') {
26 | bootApplication();
27 | } else {
28 | document.addEventListener('DOMContentLoaded', bootApplication);
29 | }
30 |
--------------------------------------------------------------------------------
/ClientApp/boot-server.ts:
--------------------------------------------------------------------------------
1 | import 'angular2-universal-polyfills';
2 | import 'angular2-universal-patch';
3 | import 'zone.js';
4 | import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
5 | import { enableProdMode } from '@angular/core';
6 | import { platformNodeDynamic } from 'angular2-universal';
7 | import { AppModule } from './app/app.module';
8 |
9 | enableProdMode();
10 | const platform = platformNodeDynamic();
11 |
12 | export default createServerRenderer(params => {
13 | return new Promise((resolve, reject) => {
14 | const requestZone = Zone.current.fork({
15 | name: 'angular-universal request',
16 | properties: {
17 | baseUrl: '/',
18 | requestUrl: params.url,
19 | originUrl: params.origin,
20 | preboot: false,
21 | document: ' '
22 | },
23 | onHandleError: (parentZone, currentZone, targetZone, error) => {
24 | // If any error occurs while rendering the module, reject the whole operation
25 | reject(error);
26 | return true;
27 | }
28 | });
29 |
30 | return requestZone.run>(() => platform.serializeModule(AppModule)).then(html => {
31 | resolve({ html: html });
32 | }, reject);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/Controllers/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/Controllers/.DS_Store
--------------------------------------------------------------------------------
/Controllers/AppPolicies.cs:
--------------------------------------------------------------------------------
1 | namespace vega.Controllers
2 | {
3 | public static class Policies
4 | {
5 | public const string RequireAdminRole = "RequireAdminRole";
6 | }
7 | }
--------------------------------------------------------------------------------
/Controllers/FeaturesController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using AutoMapper;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.EntityFrameworkCore;
7 | using vega.Controllers.Resources;
8 | using vega.Core.Models;
9 | using vega.Persistence;
10 |
11 | namespace vega.Controllers
12 | {
13 | public class FeaturesController : Controller
14 | {
15 | private readonly VegaDbContext context;
16 | private readonly IMapper mapper;
17 | public FeaturesController(VegaDbContext context, IMapper mapper)
18 | {
19 | this.mapper = mapper;
20 | this.context = context;
21 | }
22 |
23 | [HttpGet("/api/features")]
24 | public async Task> GetFeatures()
25 | {
26 | var features = await context.Features.ToListAsync();
27 |
28 | return mapper.Map, List>(features);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace Vega.Controllers
8 | {
9 | public class HomeController : Controller
10 | {
11 | public IActionResult Index()
12 | {
13 | return View();
14 | }
15 |
16 | public IActionResult Error()
17 | {
18 | return View();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Controllers/MakesController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using AutoMapper;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.EntityFrameworkCore;
6 | using vega.Controllers.Resources;
7 | using vega.Core.Models;
8 | using vega.Persistence;
9 |
10 | namespace vega.Controllers
11 | {
12 | public class MakesController : Controller
13 | {
14 | private readonly VegaDbContext context;
15 | private readonly IMapper mapper;
16 | public MakesController(VegaDbContext context, IMapper mapper)
17 | {
18 | this.mapper = mapper;
19 | this.context = context;
20 | }
21 |
22 | [HttpGet("/api/makes")]
23 | public async Task> GetMakes()
24 | {
25 | var makes = await context.Makes.Include(m => m.Models).ToListAsync();
26 |
27 | return mapper.Map, List>(makes);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Controllers/PhotosController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using AutoMapper;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.AspNetCore.Mvc;
10 | using Microsoft.Extensions.Options;
11 | using vega.Controllers.Resources;
12 | using vega.Core;
13 | using vega.Core.Models;
14 |
15 | namespace vega.Controllers
16 | {
17 | [Route("/api/vehicles/{vehicleId}/photos")]
18 | public class PhotosController : Controller
19 | {
20 | private readonly IHostingEnvironment host;
21 | private readonly IVehicleRepository vehicleRepository;
22 | private readonly IPhotoRepository photoRepository;
23 | private readonly IMapper mapper;
24 | private readonly PhotoSettings photoSettings;
25 | private readonly IPhotoService photoService;
26 |
27 | public PhotosController(IHostingEnvironment host, IVehicleRepository vehicleRepository, IPhotoRepository photoRepository, IMapper mapper, IOptionsSnapshot options, IPhotoService photoService)
28 | {
29 | this.photoService = photoService;
30 | this.photoSettings = options.Value;
31 | this.mapper = mapper;
32 | this.vehicleRepository = vehicleRepository;
33 | this.photoRepository = photoRepository;
34 | this.host = host;
35 | }
36 |
37 | [HttpGet]
38 | public async Task> GetPhotos(int vehicleId)
39 | {
40 | var photos = await photoRepository.GetPhotos(vehicleId);
41 |
42 | return mapper.Map, IEnumerable>(photos);
43 | }
44 |
45 | [HttpPost]
46 | public async Task Upload(int vehicleId, IFormFile file)
47 | {
48 | var vehicle = await vehicleRepository.GetVehicle(vehicleId, includeRelated: false);
49 | if (vehicle == null)
50 | return NotFound();
51 |
52 | if (file == null) return BadRequest("Null file");
53 | if (file.Length == 0) return BadRequest("Empty file");
54 | if (file.Length > photoSettings.MaxBytes) return BadRequest("Max file size exceeded");
55 | if (!photoSettings.IsSupported(file.FileName)) return BadRequest("Invalid file type.");
56 |
57 | var uploadsFolderPath = Path.Combine(host.WebRootPath, "uploads");
58 | var photo = await photoService.UploadPhoto(vehicle, file, uploadsFolderPath);
59 |
60 | return Ok(mapper.Map(photo));
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/Controllers/Resources/ContactResource.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace vega.Controllers.Resources
4 | {
5 | public class ContactResource
6 | {
7 | [Required]
8 | [StringLength(255)]
9 | public string Name { get; set; }
10 |
11 | [StringLength(255)]
12 | public string Email { get; set; }
13 |
14 | [Required]
15 | [StringLength(255)]
16 | public string Phone { get; set; }
17 |
18 | }
19 | }
--------------------------------------------------------------------------------
/Controllers/Resources/KeyValuePairResource.cs:
--------------------------------------------------------------------------------
1 | namespace vega.Controllers.Resources
2 | {
3 | public class KeyValuePairResource
4 | {
5 | public int Id { get; set; }
6 | public string Name { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/Controllers/Resources/MakeResource.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 |
4 | namespace vega.Controllers.Resources
5 | {
6 | public class MakeResource : KeyValuePairResource
7 | {
8 | public ICollection Models { get; set; }
9 |
10 | public MakeResource()
11 | {
12 | Models = new Collection();
13 | }
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/Controllers/Resources/PhotoResource.cs:
--------------------------------------------------------------------------------
1 | namespace vega.Controllers.Resources
2 | {
3 | public class PhotoResource
4 | {
5 | public int Id { get; set; }
6 |
7 | public string FileName { get; set; }
8 | }
9 | }
--------------------------------------------------------------------------------
/Controllers/Resources/QueryResultResource.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace vega.Controllers.Resources
4 | {
5 | public class QueryResultResource
6 | {
7 | public int TotalItems { get; set; }
8 | public IEnumerable Items { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/Controllers/Resources/SaveVehicleResource.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.ComponentModel.DataAnnotations;
4 |
5 | namespace vega.Controllers.Resources
6 | {
7 | public class SaveVehicleResource
8 | {
9 | public int Id { get; set; }
10 | public int ModelId { get; set; }
11 | public bool IsRegistered { get; set; }
12 |
13 | [Required]
14 | public ContactResource Contact { get; set; }
15 | public ICollection Features { get; set; }
16 |
17 | public SaveVehicleResource()
18 | {
19 | Features = new Collection();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Controllers/Resources/VehicleQueryResource.cs:
--------------------------------------------------------------------------------
1 | namespace vega.Controllers.Resources
2 | {
3 | public class VehicleQueryResource
4 | {
5 | public int? MakeId { get; set; }
6 | public int? ModelId { get; set; }
7 | public string SortBy { get; set; }
8 | public bool IsSortAscending { get; set; }
9 | public int Page { get; set; }
10 | public byte PageSize { get; set; }
11 | }
12 | }
--------------------------------------------------------------------------------
/Controllers/Resources/VehicleResource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 |
5 | namespace vega.Controllers.Resources
6 | {
7 | public class VehicleResource
8 | {
9 | public int Id { get; set; }
10 | public KeyValuePairResource Model { get; set; }
11 | public KeyValuePairResource Make { get; set; }
12 | public bool IsRegistered { get; set; }
13 | public ContactResource Contact { get; set; }
14 | public DateTime LastUpdate { get; set; }
15 | public ICollection Features { get; set; }
16 |
17 | public VehicleResource()
18 | {
19 | Features = new Collection();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Controllers/SampleDataController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace Vega.Controllers
8 | {
9 | [Route("api/[controller]")]
10 | public class SampleDataController : Controller
11 | {
12 | private static string[] Summaries = new[]
13 | {
14 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
15 | };
16 |
17 | [HttpGet("[action]")]
18 | public IEnumerable WeatherForecasts()
19 | {
20 | var rng = new Random();
21 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
22 | {
23 | DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
24 | TemperatureC = rng.Next(-20, 55),
25 | Summary = Summaries[rng.Next(Summaries.Length)]
26 | });
27 | }
28 |
29 | public class WeatherForecast
30 | {
31 | public string DateFormatted { get; set; }
32 | public int TemperatureC { get; set; }
33 | public string Summary { get; set; }
34 |
35 | public int TemperatureF
36 | {
37 | get
38 | {
39 | return 32 + (int)(TemperatureC / 0.5556);
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Controllers/VehiclesController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using AutoMapper;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.EntityFrameworkCore;
7 | using vega.Controllers.Resources;
8 | using vega.Core.Models;
9 | using vega.Core;
10 | using Microsoft.AspNetCore.Authorization;
11 |
12 | namespace vega.Controllers
13 | {
14 | [Route("/api/vehicles")]
15 | public class VehiclesController : Controller
16 | {
17 | private readonly IMapper mapper;
18 | private readonly IVehicleRepository repository;
19 | private readonly IUnitOfWork unitOfWork;
20 |
21 | public VehiclesController(IMapper mapper, IVehicleRepository repository, IUnitOfWork unitOfWork)
22 | {
23 | this.unitOfWork = unitOfWork;
24 | this.repository = repository;
25 | this.mapper = mapper;
26 | }
27 |
28 | [HttpPost]
29 | [Authorize]
30 | public async Task CreateVehicle([FromBody] SaveVehicleResource vehicleResource)
31 | {
32 | if (!ModelState.IsValid)
33 | return BadRequest(ModelState);
34 |
35 | var vehicle = mapper.Map(vehicleResource);
36 | vehicle.LastUpdate = DateTime.Now;
37 |
38 | repository.Add(vehicle);
39 | await unitOfWork.CompleteAsync();
40 |
41 | vehicle = await repository.GetVehicle(vehicle.Id);
42 |
43 | var result = mapper.Map(vehicle);
44 |
45 | return Ok(result);
46 | }
47 |
48 | [HttpPut("{id}")]
49 | [Authorize]
50 | public async Task UpdateVehicle(int id, [FromBody] SaveVehicleResource vehicleResource)
51 | {
52 | if (!ModelState.IsValid)
53 | return BadRequest(ModelState);
54 |
55 | var vehicle = await repository.GetVehicle(id);
56 |
57 | if (vehicle == null)
58 | return NotFound();
59 |
60 | mapper.Map(vehicleResource, vehicle);
61 | vehicle.LastUpdate = DateTime.Now;
62 |
63 | await unitOfWork.CompleteAsync();
64 |
65 | vehicle = await repository.GetVehicle(vehicle.Id);
66 | var result = mapper.Map(vehicle);
67 |
68 | return Ok(result);
69 | }
70 |
71 | [HttpDelete("{id}")]
72 | [Authorize]
73 | public async Task DeleteVehicle(int id)
74 | {
75 | var vehicle = await repository.GetVehicle(id, includeRelated: false);
76 |
77 | if (vehicle == null)
78 | return NotFound();
79 |
80 | repository.Remove(vehicle);
81 | await unitOfWork.CompleteAsync();
82 |
83 | return Ok(id);
84 | }
85 |
86 | [HttpGet("{id}")]
87 | public async Task GetVehicle(int id)
88 | {
89 | var vehicle = await repository.GetVehicle(id);
90 |
91 | if (vehicle == null)
92 | return NotFound();
93 |
94 | var vehicleResource = mapper.Map(vehicle);
95 |
96 | return Ok(vehicleResource);
97 | }
98 |
99 | [HttpGet]
100 | public async Task> GetVehicles(VehicleQueryResource filterResource)
101 | {
102 | var filter = mapper.Map(filterResource);
103 | var queryResult = await repository.GetVehicles(filter);
104 |
105 | return mapper.Map, QueryResultResource>(queryResult);
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/Core/FileSystemPhotoStorage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace vega.Core
7 | {
8 | public class FileSystemPhotoStorage : IPhotoStorage
9 | {
10 | public async Task StorePhoto(string uploadsFolderPath, IFormFile file)
11 | {
12 | if (!Directory.Exists(uploadsFolderPath))
13 | Directory.CreateDirectory(uploadsFolderPath);
14 |
15 | var fileName = Guid.NewGuid().ToString() + Path.GetExtension(file.FileName);
16 | var filePath = Path.Combine(uploadsFolderPath, fileName);
17 |
18 | using (var stream = new FileStream(filePath, FileMode.Create))
19 | {
20 | await file.CopyToAsync(stream);
21 | }
22 |
23 | return fileName;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Core/IPhotoRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using vega.Core.Models;
4 |
5 | namespace vega.Core
6 | {
7 | public interface IPhotoRepository
8 | {
9 | Task> GetPhotos(int vehicleId);
10 | }
11 | }
--------------------------------------------------------------------------------
/Core/IPhotoService.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 | using vega.Core.Models;
4 |
5 | namespace vega.Core
6 | {
7 | public interface IPhotoService
8 | {
9 | Task UploadPhoto(Vehicle vehicle, IFormFile file, string uploadsFolderPath);
10 | }
11 | }
--------------------------------------------------------------------------------
/Core/IPhotoStorage.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace vega.Core
5 | {
6 | public interface IPhotoStorage
7 | {
8 | Task StorePhoto(string uploadsFolderPath, IFormFile file);
9 | }
10 | }
--------------------------------------------------------------------------------
/Core/IUnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace vega.Core
5 | {
6 | public interface IUnitOfWork
7 | {
8 | Task CompleteAsync();
9 | }
10 | }
--------------------------------------------------------------------------------
/Core/IVehicleRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using vega.Core.Models;
4 |
5 | namespace vega.Core
6 | {
7 | public interface IVehicleRepository
8 | {
9 | Task GetVehicle(int id, bool includeRelated = true);
10 | void Add(Vehicle vehicle);
11 | void Remove(Vehicle vehicle);
12 | Task> GetVehicles(VehicleQuery filter);
13 | }
14 | }
--------------------------------------------------------------------------------
/Core/Models/Feature.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace vega.Core.Models
4 | {
5 | public class Feature
6 | {
7 | public int Id { get; set; }
8 |
9 | [Required]
10 | [StringLength(255)]
11 | public string Name { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/Core/Models/Make.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.ComponentModel.DataAnnotations;
4 |
5 | namespace vega.Core.Models
6 | {
7 | public class Make
8 | {
9 | public int Id { get; set; }
10 | [Required]
11 | [StringLength(255)]
12 | public string Name { get; set; }
13 | public ICollection Models { get; set; }
14 |
15 | public Make()
16 | {
17 | Models = new Collection();
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Core/Models/Model.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.ComponentModel.DataAnnotations.Schema;
3 |
4 | namespace vega.Core.Models
5 | {
6 | [Table("Models")]
7 | public class Model
8 | {
9 | public int Id { get; set; }
10 |
11 | [Required]
12 | [StringLength(255)]
13 | public string Name { get; set; }
14 |
15 | public Make Make { get; set; }
16 |
17 | public int MakeId { get; set; }
18 | }
19 | }
--------------------------------------------------------------------------------
/Core/Models/Photo.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace vega.Core.Models
4 | {
5 | public class Photo
6 | {
7 | public int Id { get; set; }
8 |
9 | [Required]
10 | [StringLength(255)]
11 | public string FileName { get; set; }
12 |
13 | public int VehicleId { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/Core/Models/PhotoSettings.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 |
4 | namespace vega.Core.Models
5 | {
6 | public class PhotoSettings
7 | {
8 | public int MaxBytes { get; set; }
9 | public string[] AcceptedFileTypes { get; set; }
10 |
11 | public bool IsSupported(string fileName)
12 | {
13 | return AcceptedFileTypes.Any(s => s == Path.GetExtension(fileName).ToLower());
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/Core/Models/QueryResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace vega.Core.Models
4 | {
5 | public class QueryResult
6 | {
7 | public int TotalItems { get; set; }
8 | public IEnumerable Items { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/Core/Models/Vehicle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel.DataAnnotations;
5 | using System.ComponentModel.DataAnnotations.Schema;
6 |
7 | namespace vega.Core.Models
8 | {
9 | [Table("Vehicles")]
10 | public class Vehicle
11 | {
12 | public int Id { get; set; }
13 | public int ModelId { get; set; }
14 | public Model Model { get; set; }
15 | public bool IsRegistered { get; set; }
16 | [Required]
17 | [StringLength(255)]
18 | public string ContactName { get; set; }
19 |
20 | [StringLength(255)]
21 | public string ContactEmail { get; set; }
22 |
23 | [Required]
24 | [StringLength(255)]
25 | public string ContactPhone { get; set; }
26 | public DateTime LastUpdate { get; set; }
27 | public ICollection Features { get; set; }
28 | public ICollection Photos { get; set; }
29 |
30 | public Vehicle()
31 | {
32 | Features = new Collection();
33 | Photos = new Collection();
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Core/Models/VehicleFeature.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations.Schema;
2 |
3 | namespace vega.Core.Models
4 | {
5 | [Table("VehicleFeatures")]
6 | public class VehicleFeature
7 | {
8 | public int VehicleId { get; set; }
9 | public int FeatureId { get; set; }
10 | public Vehicle Vehicle { get; set; }
11 | public Feature Feature { get; set; }
12 | }
13 | }
--------------------------------------------------------------------------------
/Core/Models/VehicleQuery.cs:
--------------------------------------------------------------------------------
1 | using vega.Extensions;
2 |
3 | namespace vega.Core.Models
4 | {
5 | public class VehicleQuery : IQueryObject
6 | {
7 | public int? MakeId { get; set; }
8 | public int? ModelId { get; set; }
9 | public string SortBy { get; set; }
10 | public bool IsSortAscending { get; set; }
11 | public int Page { get; set; }
12 | public byte PageSize { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/Core/PhotoService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Http;
5 | using vega.Core.Models;
6 |
7 | namespace vega.Core
8 | {
9 | public class PhotoService : IPhotoService
10 | {
11 | private readonly IUnitOfWork unitOfWork;
12 | private readonly IPhotoStorage photoStorage;
13 | public PhotoService(IUnitOfWork unitOfWork, IPhotoStorage photoStorage)
14 | {
15 | this.photoStorage = photoStorage;
16 | this.unitOfWork = unitOfWork;
17 | }
18 |
19 | public async Task UploadPhoto(Vehicle vehicle, IFormFile file, string uploadsFolderPath)
20 | {
21 | var fileName = await photoStorage.StorePhoto(uploadsFolderPath, file);
22 |
23 | var photo = new Photo { FileName = fileName };
24 | vehicle.Photos.Add(photo);
25 | await unitOfWork.CompleteAsync();
26 |
27 | return photo;
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/Extensions/IQueryObject.cs:
--------------------------------------------------------------------------------
1 | namespace vega.Extensions
2 | {
3 | public interface IQueryObject
4 | {
5 | string SortBy { get; set; }
6 | bool IsSortAscending { get; set; }
7 | int Page { get; set; }
8 | byte PageSize { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/Extensions/IQueryableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using vega.Core.Models;
6 |
7 | namespace vega.Extensions
8 | {
9 | public static class IQueryableExtensions
10 | {
11 |
12 | public static IQueryable ApplyFiltering(this IQueryable query, VehicleQuery queryObj)
13 | {
14 | if (queryObj.MakeId.HasValue)
15 | query = query.Where(v => v.Model.MakeId == queryObj.MakeId.Value);
16 |
17 | if (queryObj.ModelId.HasValue)
18 | query = query.Where(v => v.ModelId == queryObj.ModelId.Value);
19 |
20 | return query;
21 | }
22 |
23 | public static IQueryable ApplyOrdering(this IQueryable query, IQueryObject queryObj, Dictionary>> columnsMap)
24 | {
25 | if (String.IsNullOrWhiteSpace(queryObj.SortBy) || !columnsMap.ContainsKey(queryObj.SortBy))
26 | return query;
27 |
28 | if (queryObj.IsSortAscending)
29 | return query.OrderBy(columnsMap[queryObj.SortBy]);
30 | else
31 | return query.OrderByDescending(columnsMap[queryObj.SortBy]);
32 | }
33 |
34 | public static IQueryable ApplyPaging(this IQueryable query, IQueryObject queryObj)
35 | {
36 | if (queryObj.Page <= 0)
37 | queryObj.Page = 1;
38 |
39 | if (queryObj.PageSize <= 0)
40 | queryObj.PageSize = 10;
41 |
42 | return query.Skip((queryObj.Page - 1) * queryObj.PageSize).Take(queryObj.PageSize);
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/Mapping/MappingProfile.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using System.Linq;
3 | using vega.Controllers.Resources;
4 | using vega.Core.Models;
5 |
6 | namespace vega.Mapping
7 | {
8 | public class MappingProfile : Profile
9 | {
10 | public MappingProfile()
11 | {
12 | // Domain to API Resource
13 | CreateMap();
14 | CreateMap(typeof(QueryResult<>), typeof(QueryResultResource<>));
15 | CreateMap();
16 | CreateMap();
17 | CreateMap();
18 | CreateMap();
19 | CreateMap()
20 | .ForMember(vr => vr.Contact, opt => opt.MapFrom(v => new ContactResource { Name = v.ContactName, Email = v.ContactEmail, Phone = v.ContactPhone } ))
21 | .ForMember(vr => vr.Features, opt => opt.MapFrom(v => v.Features.Select(vf => vf.FeatureId)));
22 | CreateMap()
23 | .ForMember(vr => vr.Make, opt => opt.MapFrom(v => v.Model.Make))
24 | .ForMember(vr => vr.Contact, opt => opt.MapFrom(v => new ContactResource { Name = v.ContactName, Email = v.ContactEmail, Phone = v.ContactPhone } ))
25 | .ForMember(vr => vr.Features, opt => opt.MapFrom(v => v.Features.Select(vf => new KeyValuePairResource { Id = vf.Feature.Id, Name = vf.Feature.Name })));
26 |
27 | // API Resource to Domain
28 | CreateMap();
29 | CreateMap()
30 | .ForMember(v => v.Id, opt => opt.Ignore())
31 | .ForMember(v => v.ContactName, opt => opt.MapFrom(vr => vr.Contact.Name))
32 | .ForMember(v => v.ContactEmail, opt => opt.MapFrom(vr => vr.Contact.Email))
33 | .ForMember(v => v.ContactPhone, opt => opt.MapFrom(vr => vr.Contact.Phone))
34 | .ForMember(v => v.Features, opt => opt.Ignore())
35 | .AfterMap((vr, v) => {
36 | // Remove unselected features
37 | var removedFeatures = v.Features.Where(f => !vr.Features.Contains(f.FeatureId)).ToList();
38 | foreach (var f in removedFeatures)
39 | v.Features.Remove(f);
40 |
41 | // Add new features
42 | var addedFeatures = vr.Features.Where(id => !v.Features.Any(f => f.FeatureId == id)).Select(id => new VehicleFeature { FeatureId = id }).ToList();
43 | foreach (var f in addedFeatures)
44 | v.Features.Add(f);
45 | });
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Migrations/20170320232928_InitialModel.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170320232928_InitialModel")]
12 | partial class InitialModel
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Make", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name");
26 |
27 | b.HasKey("Id");
28 |
29 | b.ToTable("Makes");
30 | });
31 |
32 | modelBuilder.Entity("vega.Models.Model", b =>
33 | {
34 | b.Property("Id")
35 | .ValueGeneratedOnAdd();
36 |
37 | b.Property("MakeId");
38 |
39 | b.Property("Name");
40 |
41 | b.HasKey("Id");
42 |
43 | b.HasIndex("MakeId");
44 |
45 | b.ToTable("Model");
46 | });
47 |
48 | modelBuilder.Entity("vega.Models.Model", b =>
49 | {
50 | b.HasOne("vega.Models.Make", "Make")
51 | .WithMany("Models")
52 | .HasForeignKey("MakeId")
53 | .OnDelete(DeleteBehavior.Cascade);
54 | });
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Migrations/20170320232928_InitialModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 |
6 | namespace Vega.Migrations
7 | {
8 | public partial class InitialModel : Migration
9 | {
10 | protected override void Up(MigrationBuilder migrationBuilder)
11 | {
12 | migrationBuilder.CreateTable(
13 | name: "Makes",
14 | columns: table => new
15 | {
16 | Id = table.Column(nullable: false)
17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
18 | Name = table.Column(nullable: true)
19 | },
20 | constraints: table =>
21 | {
22 | table.PrimaryKey("PK_Makes", x => x.Id);
23 | });
24 |
25 | migrationBuilder.CreateTable(
26 | name: "Model",
27 | columns: table => new
28 | {
29 | Id = table.Column(nullable: false)
30 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
31 | MakeId = table.Column(nullable: false),
32 | Name = table.Column(nullable: true)
33 | },
34 | constraints: table =>
35 | {
36 | table.PrimaryKey("PK_Model", x => x.Id);
37 | table.ForeignKey(
38 | name: "FK_Model_Makes_MakeId",
39 | column: x => x.MakeId,
40 | principalTable: "Makes",
41 | principalColumn: "Id",
42 | onDelete: ReferentialAction.Cascade);
43 | });
44 |
45 | migrationBuilder.CreateIndex(
46 | name: "IX_Model_MakeId",
47 | table: "Model",
48 | column: "MakeId");
49 | }
50 |
51 | protected override void Down(MigrationBuilder migrationBuilder)
52 | {
53 | migrationBuilder.DropTable(
54 | name: "Model");
55 |
56 | migrationBuilder.DropTable(
57 | name: "Makes");
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Migrations/20170321005120_ApplyConstraints.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170321005120_ApplyConstraints")]
12 | partial class ApplyConstraints
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Make", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Makes");
32 | });
33 |
34 | modelBuilder.Entity("vega.Models.Model", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("MakeId");
40 |
41 | b.Property("Name")
42 | .IsRequired()
43 | .HasMaxLength(255);
44 |
45 | b.HasKey("Id");
46 |
47 | b.HasIndex("MakeId");
48 |
49 | b.ToTable("Models");
50 | });
51 |
52 | modelBuilder.Entity("vega.Models.Model", b =>
53 | {
54 | b.HasOne("vega.Models.Make", "Make")
55 | .WithMany("Models")
56 | .HasForeignKey("MakeId")
57 | .OnDelete(DeleteBehavior.Cascade);
58 | });
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Migrations/20170321005120_ApplyConstraints.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | namespace Vega.Migrations
6 | {
7 | public partial class ApplyConstraints : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.DropForeignKey(
12 | name: "FK_Model_Makes_MakeId",
13 | table: "Model");
14 |
15 | migrationBuilder.DropPrimaryKey(
16 | name: "PK_Model",
17 | table: "Model");
18 |
19 | migrationBuilder.RenameTable(
20 | name: "Model",
21 | newName: "Models");
22 |
23 | migrationBuilder.RenameIndex(
24 | name: "IX_Model_MakeId",
25 | table: "Models",
26 | newName: "IX_Models_MakeId");
27 |
28 | migrationBuilder.AlterColumn(
29 | name: "Name",
30 | table: "Models",
31 | maxLength: 255,
32 | nullable: false,
33 | oldClrType: typeof(string),
34 | oldNullable: true);
35 |
36 | migrationBuilder.AlterColumn(
37 | name: "Name",
38 | table: "Makes",
39 | maxLength: 255,
40 | nullable: false,
41 | oldClrType: typeof(string),
42 | oldNullable: true);
43 |
44 | migrationBuilder.AddPrimaryKey(
45 | name: "PK_Models",
46 | table: "Models",
47 | column: "Id");
48 |
49 | migrationBuilder.AddForeignKey(
50 | name: "FK_Models_Makes_MakeId",
51 | table: "Models",
52 | column: "MakeId",
53 | principalTable: "Makes",
54 | principalColumn: "Id",
55 | onDelete: ReferentialAction.Cascade);
56 | }
57 |
58 | protected override void Down(MigrationBuilder migrationBuilder)
59 | {
60 | migrationBuilder.DropForeignKey(
61 | name: "FK_Models_Makes_MakeId",
62 | table: "Models");
63 |
64 | migrationBuilder.DropPrimaryKey(
65 | name: "PK_Models",
66 | table: "Models");
67 |
68 | migrationBuilder.RenameTable(
69 | name: "Models",
70 | newName: "Model");
71 |
72 | migrationBuilder.RenameIndex(
73 | name: "IX_Models_MakeId",
74 | table: "Model",
75 | newName: "IX_Model_MakeId");
76 |
77 | migrationBuilder.AlterColumn(
78 | name: "Name",
79 | table: "Model",
80 | nullable: true,
81 | oldClrType: typeof(string),
82 | oldMaxLength: 255);
83 |
84 | migrationBuilder.AlterColumn(
85 | name: "Name",
86 | table: "Makes",
87 | nullable: true,
88 | oldClrType: typeof(string),
89 | oldMaxLength: 255);
90 |
91 | migrationBuilder.AddPrimaryKey(
92 | name: "PK_Model",
93 | table: "Model",
94 | column: "Id");
95 |
96 | migrationBuilder.AddForeignKey(
97 | name: "FK_Model_Makes_MakeId",
98 | table: "Model",
99 | column: "MakeId",
100 | principalTable: "Makes",
101 | principalColumn: "Id",
102 | onDelete: ReferentialAction.Cascade);
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Migrations/20170321005742_SeedDatabase.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170321005742_SeedDatabase")]
12 | partial class SeedDatabase
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Make", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Makes");
32 | });
33 |
34 | modelBuilder.Entity("vega.Models.Model", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("MakeId");
40 |
41 | b.Property("Name")
42 | .IsRequired()
43 | .HasMaxLength(255);
44 |
45 | b.HasKey("Id");
46 |
47 | b.HasIndex("MakeId");
48 |
49 | b.ToTable("Models");
50 | });
51 |
52 | modelBuilder.Entity("vega.Models.Model", b =>
53 | {
54 | b.HasOne("vega.Models.Make", "Make")
55 | .WithMany("Models")
56 | .HasForeignKey("MakeId")
57 | .OnDelete(DeleteBehavior.Cascade);
58 | });
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Migrations/20170321005742_SeedDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | namespace Vega.Migrations
6 | {
7 | public partial class SeedDatabase : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.Sql("INSERT INTO Makes (Name) VALUES ('Make1')");
12 | migrationBuilder.Sql("INSERT INTO Makes (Name) VALUES ('Make2')");
13 | migrationBuilder.Sql("INSERT INTO Makes (Name) VALUES ('Make3')");
14 |
15 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make1-ModelA', (SELECT ID FROM Makes WHERE Name = 'Make1'))");
16 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make1-ModelB', (SELECT ID FROM Makes WHERE Name = 'Make1'))");
17 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make1-ModelC', (SELECT ID FROM Makes WHERE Name = 'Make1'))");
18 |
19 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make2-ModelA', (SELECT ID FROM Makes WHERE Name = 'Make2'))");
20 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make2-ModelB', (SELECT ID FROM Makes WHERE Name = 'Make2'))");
21 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make2-ModelC', (SELECT ID FROM Makes WHERE Name = 'Make2'))");
22 |
23 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make3-ModelA', (SELECT ID FROM Makes WHERE Name = 'Make3'))");
24 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make3-ModelB', (SELECT ID FROM Makes WHERE Name = 'Make3'))");
25 | migrationBuilder.Sql("INSERT INTO Models (Name, MakeID) VALUES ('Make3-ModelC', (SELECT ID FROM Makes WHERE Name = 'Make3'))");
26 |
27 | }
28 |
29 | protected override void Down(MigrationBuilder migrationBuilder)
30 | {
31 | migrationBuilder.Sql("DELETE FROM Makes WHERE Name IN ('Make1', 'Make2', 'Make3')");
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Migrations/20170322022552_AddFeature.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170322022552_AddFeature")]
12 | partial class AddFeature
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Feature", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Features");
32 | });
33 |
34 | modelBuilder.Entity("vega.Models.Make", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("Name")
40 | .IsRequired()
41 | .HasMaxLength(255);
42 |
43 | b.HasKey("Id");
44 |
45 | b.ToTable("Makes");
46 | });
47 |
48 | modelBuilder.Entity("vega.Models.Model", b =>
49 | {
50 | b.Property("Id")
51 | .ValueGeneratedOnAdd();
52 |
53 | b.Property("MakeId");
54 |
55 | b.Property("Name")
56 | .IsRequired()
57 | .HasMaxLength(255);
58 |
59 | b.HasKey("Id");
60 |
61 | b.HasIndex("MakeId");
62 |
63 | b.ToTable("Models");
64 | });
65 |
66 | modelBuilder.Entity("vega.Models.Model", b =>
67 | {
68 | b.HasOne("vega.Models.Make", "Make")
69 | .WithMany("Models")
70 | .HasForeignKey("MakeId")
71 | .OnDelete(DeleteBehavior.Cascade);
72 | });
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Migrations/20170322022552_AddFeature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 |
6 | namespace Vega.Migrations
7 | {
8 | public partial class AddFeature : Migration
9 | {
10 | protected override void Up(MigrationBuilder migrationBuilder)
11 | {
12 | migrationBuilder.CreateTable(
13 | name: "Features",
14 | columns: table => new
15 | {
16 | Id = table.Column(nullable: false)
17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
18 | Name = table.Column(maxLength: 255, nullable: false)
19 | },
20 | constraints: table =>
21 | {
22 | table.PrimaryKey("PK_Features", x => x.Id);
23 | });
24 | }
25 |
26 | protected override void Down(MigrationBuilder migrationBuilder)
27 | {
28 | migrationBuilder.DropTable(
29 | name: "Features");
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Migrations/20170322022625_SeedFeatures.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170322022625_SeedFeatures")]
12 | partial class SeedFeatures
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Feature", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Features");
32 | });
33 |
34 | modelBuilder.Entity("vega.Models.Make", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("Name")
40 | .IsRequired()
41 | .HasMaxLength(255);
42 |
43 | b.HasKey("Id");
44 |
45 | b.ToTable("Makes");
46 | });
47 |
48 | modelBuilder.Entity("vega.Models.Model", b =>
49 | {
50 | b.Property("Id")
51 | .ValueGeneratedOnAdd();
52 |
53 | b.Property("MakeId");
54 |
55 | b.Property("Name")
56 | .IsRequired()
57 | .HasMaxLength(255);
58 |
59 | b.HasKey("Id");
60 |
61 | b.HasIndex("MakeId");
62 |
63 | b.ToTable("Models");
64 | });
65 |
66 | modelBuilder.Entity("vega.Models.Model", b =>
67 | {
68 | b.HasOne("vega.Models.Make", "Make")
69 | .WithMany("Models")
70 | .HasForeignKey("MakeId")
71 | .OnDelete(DeleteBehavior.Cascade);
72 | });
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Migrations/20170322022625_SeedFeatures.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 |
5 | namespace Vega.Migrations
6 | {
7 | public partial class SeedFeatures : Migration
8 | {
9 | protected override void Up(MigrationBuilder migrationBuilder)
10 | {
11 | migrationBuilder.Sql("INSERT INTO Features (Name) VALUES ('Feature1')");
12 | migrationBuilder.Sql("INSERT INTO Features (Name) VALUES ('Feature2')");
13 | migrationBuilder.Sql("INSERT INTO Features (Name) VALUES ('Feature3')");
14 | }
15 |
16 | protected override void Down(MigrationBuilder migrationBuilder)
17 | {
18 | migrationBuilder.Sql("DELETE FROM Features WHERE Name IN ('Feature1', 'Feature2', 'Feature3')");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Migrations/20170326223931_AddVehicle.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170326223931_AddVehicle")]
12 | partial class AddVehicle
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Models.Feature", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Features");
32 | });
33 |
34 | modelBuilder.Entity("vega.Models.Make", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("Name")
40 | .IsRequired()
41 | .HasMaxLength(255);
42 |
43 | b.HasKey("Id");
44 |
45 | b.ToTable("Makes");
46 | });
47 |
48 | modelBuilder.Entity("vega.Models.Model", b =>
49 | {
50 | b.Property("Id")
51 | .ValueGeneratedOnAdd();
52 |
53 | b.Property("MakeId");
54 |
55 | b.Property("Name")
56 | .IsRequired()
57 | .HasMaxLength(255);
58 |
59 | b.HasKey("Id");
60 |
61 | b.HasIndex("MakeId");
62 |
63 | b.ToTable("Models");
64 | });
65 |
66 | modelBuilder.Entity("vega.Models.Vehicle", b =>
67 | {
68 | b.Property("Id")
69 | .ValueGeneratedOnAdd();
70 |
71 | b.Property("ContactEmail")
72 | .HasMaxLength(255);
73 |
74 | b.Property("ContactName")
75 | .IsRequired()
76 | .HasMaxLength(255);
77 |
78 | b.Property("ContactPhone")
79 | .IsRequired()
80 | .HasMaxLength(255);
81 |
82 | b.Property("IsRegistered");
83 |
84 | b.Property("LastUpdate");
85 |
86 | b.Property("ModelId");
87 |
88 | b.HasKey("Id");
89 |
90 | b.HasIndex("ModelId");
91 |
92 | b.ToTable("Vehicles");
93 | });
94 |
95 | modelBuilder.Entity("vega.Models.VehicleFeature", b =>
96 | {
97 | b.Property("VehicleId");
98 |
99 | b.Property("FeatureId");
100 |
101 | b.HasKey("VehicleId", "FeatureId");
102 |
103 | b.HasIndex("FeatureId");
104 |
105 | b.ToTable("VehicleFeatures");
106 | });
107 |
108 | modelBuilder.Entity("vega.Models.Model", b =>
109 | {
110 | b.HasOne("vega.Models.Make", "Make")
111 | .WithMany("Models")
112 | .HasForeignKey("MakeId")
113 | .OnDelete(DeleteBehavior.Cascade);
114 | });
115 |
116 | modelBuilder.Entity("vega.Models.Vehicle", b =>
117 | {
118 | b.HasOne("vega.Models.Model", "Model")
119 | .WithMany()
120 | .HasForeignKey("ModelId")
121 | .OnDelete(DeleteBehavior.Cascade);
122 | });
123 |
124 | modelBuilder.Entity("vega.Models.VehicleFeature", b =>
125 | {
126 | b.HasOne("vega.Models.Feature", "Feature")
127 | .WithMany()
128 | .HasForeignKey("FeatureId")
129 | .OnDelete(DeleteBehavior.Cascade);
130 |
131 | b.HasOne("vega.Models.Vehicle", "Vehicle")
132 | .WithMany("Features")
133 | .HasForeignKey("VehicleId")
134 | .OnDelete(DeleteBehavior.Cascade);
135 | });
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Migrations/20170326223931_AddVehicle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 |
6 | namespace Vega.Migrations
7 | {
8 | public partial class AddVehicle : Migration
9 | {
10 | protected override void Up(MigrationBuilder migrationBuilder)
11 | {
12 | migrationBuilder.CreateTable(
13 | name: "Vehicles",
14 | columns: table => new
15 | {
16 | Id = table.Column(nullable: false)
17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
18 | ContactEmail = table.Column(maxLength: 255, nullable: true),
19 | ContactName = table.Column(maxLength: 255, nullable: false),
20 | ContactPhone = table.Column(maxLength: 255, nullable: false),
21 | IsRegistered = table.Column(nullable: false),
22 | LastUpdate = table.Column(nullable: false),
23 | ModelId = table.Column(nullable: false)
24 | },
25 | constraints: table =>
26 | {
27 | table.PrimaryKey("PK_Vehicles", x => x.Id);
28 | table.ForeignKey(
29 | name: "FK_Vehicles_Models_ModelId",
30 | column: x => x.ModelId,
31 | principalTable: "Models",
32 | principalColumn: "Id",
33 | onDelete: ReferentialAction.Cascade);
34 | });
35 |
36 | migrationBuilder.CreateTable(
37 | name: "VehicleFeatures",
38 | columns: table => new
39 | {
40 | VehicleId = table.Column(nullable: false),
41 | FeatureId = table.Column(nullable: false)
42 | },
43 | constraints: table =>
44 | {
45 | table.PrimaryKey("PK_VehicleFeatures", x => new { x.VehicleId, x.FeatureId });
46 | table.ForeignKey(
47 | name: "FK_VehicleFeatures_Features_FeatureId",
48 | column: x => x.FeatureId,
49 | principalTable: "Features",
50 | principalColumn: "Id",
51 | onDelete: ReferentialAction.Cascade);
52 | table.ForeignKey(
53 | name: "FK_VehicleFeatures_Vehicles_VehicleId",
54 | column: x => x.VehicleId,
55 | principalTable: "Vehicles",
56 | principalColumn: "Id",
57 | onDelete: ReferentialAction.Cascade);
58 | });
59 |
60 | migrationBuilder.CreateIndex(
61 | name: "IX_Vehicles_ModelId",
62 | table: "Vehicles",
63 | column: "ModelId");
64 |
65 | migrationBuilder.CreateIndex(
66 | name: "IX_VehicleFeatures_FeatureId",
67 | table: "VehicleFeatures",
68 | column: "FeatureId");
69 | }
70 |
71 | protected override void Down(MigrationBuilder migrationBuilder)
72 | {
73 | migrationBuilder.DropTable(
74 | name: "VehicleFeatures");
75 |
76 | migrationBuilder.DropTable(
77 | name: "Vehicles");
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Migrations/20170412005215_AddPhoto.Designer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | [Migration("20170412005215_AddPhoto")]
12 | partial class AddPhoto
13 | {
14 | protected override void BuildTargetModel(ModelBuilder modelBuilder)
15 | {
16 | modelBuilder
17 | .HasAnnotation("ProductVersion", "1.1.1")
18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
19 |
20 | modelBuilder.Entity("vega.Core.Models.Feature", b =>
21 | {
22 | b.Property("Id")
23 | .ValueGeneratedOnAdd();
24 |
25 | b.Property("Name")
26 | .IsRequired()
27 | .HasMaxLength(255);
28 |
29 | b.HasKey("Id");
30 |
31 | b.ToTable("Features");
32 | });
33 |
34 | modelBuilder.Entity("vega.Core.Models.Make", b =>
35 | {
36 | b.Property("Id")
37 | .ValueGeneratedOnAdd();
38 |
39 | b.Property("Name")
40 | .IsRequired()
41 | .HasMaxLength(255);
42 |
43 | b.HasKey("Id");
44 |
45 | b.ToTable("Makes");
46 | });
47 |
48 | modelBuilder.Entity("vega.Core.Models.Model", b =>
49 | {
50 | b.Property("Id")
51 | .ValueGeneratedOnAdd();
52 |
53 | b.Property("MakeId");
54 |
55 | b.Property("Name")
56 | .IsRequired()
57 | .HasMaxLength(255);
58 |
59 | b.HasKey("Id");
60 |
61 | b.HasIndex("MakeId");
62 |
63 | b.ToTable("Models");
64 | });
65 |
66 | modelBuilder.Entity("vega.Core.Models.Photo", b =>
67 | {
68 | b.Property("Id")
69 | .ValueGeneratedOnAdd();
70 |
71 | b.Property("FileName")
72 | .IsRequired()
73 | .HasMaxLength(255);
74 |
75 | b.Property("VehicleId");
76 |
77 | b.HasKey("Id");
78 |
79 | b.HasIndex("VehicleId");
80 |
81 | b.ToTable("Photos");
82 | });
83 |
84 | modelBuilder.Entity("vega.Core.Models.Vehicle", b =>
85 | {
86 | b.Property("Id")
87 | .ValueGeneratedOnAdd();
88 |
89 | b.Property("ContactEmail")
90 | .HasMaxLength(255);
91 |
92 | b.Property("ContactName")
93 | .IsRequired()
94 | .HasMaxLength(255);
95 |
96 | b.Property("ContactPhone")
97 | .IsRequired()
98 | .HasMaxLength(255);
99 |
100 | b.Property("IsRegistered");
101 |
102 | b.Property("LastUpdate");
103 |
104 | b.Property("ModelId");
105 |
106 | b.HasKey("Id");
107 |
108 | b.HasIndex("ModelId");
109 |
110 | b.ToTable("Vehicles");
111 | });
112 |
113 | modelBuilder.Entity("vega.Core.Models.VehicleFeature", b =>
114 | {
115 | b.Property("VehicleId");
116 |
117 | b.Property("FeatureId");
118 |
119 | b.HasKey("VehicleId", "FeatureId");
120 |
121 | b.HasIndex("FeatureId");
122 |
123 | b.ToTable("VehicleFeatures");
124 | });
125 |
126 | modelBuilder.Entity("vega.Core.Models.Model", b =>
127 | {
128 | b.HasOne("vega.Core.Models.Make", "Make")
129 | .WithMany("Models")
130 | .HasForeignKey("MakeId")
131 | .OnDelete(DeleteBehavior.Cascade);
132 | });
133 |
134 | modelBuilder.Entity("vega.Core.Models.Photo", b =>
135 | {
136 | b.HasOne("vega.Core.Models.Vehicle")
137 | .WithMany("Photos")
138 | .HasForeignKey("VehicleId");
139 | });
140 |
141 | modelBuilder.Entity("vega.Core.Models.Vehicle", b =>
142 | {
143 | b.HasOne("vega.Core.Models.Model", "Model")
144 | .WithMany()
145 | .HasForeignKey("ModelId")
146 | .OnDelete(DeleteBehavior.Cascade);
147 | });
148 |
149 | modelBuilder.Entity("vega.Core.Models.VehicleFeature", b =>
150 | {
151 | b.HasOne("vega.Core.Models.Feature", "Feature")
152 | .WithMany()
153 | .HasForeignKey("FeatureId")
154 | .OnDelete(DeleteBehavior.Cascade);
155 |
156 | b.HasOne("vega.Core.Models.Vehicle", "Vehicle")
157 | .WithMany("Features")
158 | .HasForeignKey("VehicleId")
159 | .OnDelete(DeleteBehavior.Cascade);
160 | });
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/Migrations/20170412005215_AddPhoto.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.EntityFrameworkCore.Migrations;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 |
6 | namespace Vega.Migrations
7 | {
8 | public partial class AddPhoto : Migration
9 | {
10 | protected override void Up(MigrationBuilder migrationBuilder)
11 | {
12 | migrationBuilder.CreateTable(
13 | name: "Photos",
14 | columns: table => new
15 | {
16 | Id = table.Column(nullable: false)
17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
18 | FileName = table.Column(maxLength: 255, nullable: false),
19 | VehicleId = table.Column(nullable: true)
20 | },
21 | constraints: table =>
22 | {
23 | table.PrimaryKey("PK_Photos", x => x.Id);
24 | table.ForeignKey(
25 | name: "FK_Photos_Vehicles_VehicleId",
26 | column: x => x.VehicleId,
27 | principalTable: "Vehicles",
28 | principalColumn: "Id",
29 | onDelete: ReferentialAction.Restrict);
30 | });
31 |
32 | migrationBuilder.CreateIndex(
33 | name: "IX_Photos_VehicleId",
34 | table: "Photos",
35 | column: "VehicleId");
36 | }
37 |
38 | protected override void Down(MigrationBuilder migrationBuilder)
39 | {
40 | migrationBuilder.DropTable(
41 | name: "Photos");
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Migrations/VegaDbContextModelSnapshot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore.Infrastructure;
4 | using Microsoft.EntityFrameworkCore.Metadata;
5 | using Microsoft.EntityFrameworkCore.Migrations;
6 | using vega.Persistence;
7 |
8 | namespace Vega.Migrations
9 | {
10 | [DbContext(typeof(VegaDbContext))]
11 | partial class VegaDbContextModelSnapshot : ModelSnapshot
12 | {
13 | protected override void BuildModel(ModelBuilder modelBuilder)
14 | {
15 | modelBuilder
16 | .HasAnnotation("ProductVersion", "1.1.1")
17 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
18 |
19 | modelBuilder.Entity("vega.Core.Models.Feature", b =>
20 | {
21 | b.Property("Id")
22 | .ValueGeneratedOnAdd();
23 |
24 | b.Property("Name")
25 | .IsRequired()
26 | .HasMaxLength(255);
27 |
28 | b.HasKey("Id");
29 |
30 | b.ToTable("Features");
31 | });
32 |
33 | modelBuilder.Entity("vega.Core.Models.Make", b =>
34 | {
35 | b.Property("Id")
36 | .ValueGeneratedOnAdd();
37 |
38 | b.Property("Name")
39 | .IsRequired()
40 | .HasMaxLength(255);
41 |
42 | b.HasKey("Id");
43 |
44 | b.ToTable("Makes");
45 | });
46 |
47 | modelBuilder.Entity("vega.Core.Models.Model", b =>
48 | {
49 | b.Property("Id")
50 | .ValueGeneratedOnAdd();
51 |
52 | b.Property("MakeId");
53 |
54 | b.Property("Name")
55 | .IsRequired()
56 | .HasMaxLength(255);
57 |
58 | b.HasKey("Id");
59 |
60 | b.HasIndex("MakeId");
61 |
62 | b.ToTable("Models");
63 | });
64 |
65 | modelBuilder.Entity("vega.Core.Models.Photo", b =>
66 | {
67 | b.Property("Id")
68 | .ValueGeneratedOnAdd();
69 |
70 | b.Property("FileName")
71 | .IsRequired()
72 | .HasMaxLength(255);
73 |
74 | b.Property("VehicleId");
75 |
76 | b.HasKey("Id");
77 |
78 | b.HasIndex("VehicleId");
79 |
80 | b.ToTable("Photos");
81 | });
82 |
83 | modelBuilder.Entity("vega.Core.Models.Vehicle", b =>
84 | {
85 | b.Property("Id")
86 | .ValueGeneratedOnAdd();
87 |
88 | b.Property("ContactEmail")
89 | .HasMaxLength(255);
90 |
91 | b.Property("ContactName")
92 | .IsRequired()
93 | .HasMaxLength(255);
94 |
95 | b.Property("ContactPhone")
96 | .IsRequired()
97 | .HasMaxLength(255);
98 |
99 | b.Property("IsRegistered");
100 |
101 | b.Property("LastUpdate");
102 |
103 | b.Property("ModelId");
104 |
105 | b.HasKey("Id");
106 |
107 | b.HasIndex("ModelId");
108 |
109 | b.ToTable("Vehicles");
110 | });
111 |
112 | modelBuilder.Entity("vega.Core.Models.VehicleFeature", b =>
113 | {
114 | b.Property("VehicleId");
115 |
116 | b.Property("FeatureId");
117 |
118 | b.HasKey("VehicleId", "FeatureId");
119 |
120 | b.HasIndex("FeatureId");
121 |
122 | b.ToTable("VehicleFeatures");
123 | });
124 |
125 | modelBuilder.Entity("vega.Core.Models.Model", b =>
126 | {
127 | b.HasOne("vega.Core.Models.Make", "Make")
128 | .WithMany("Models")
129 | .HasForeignKey("MakeId")
130 | .OnDelete(DeleteBehavior.Cascade);
131 | });
132 |
133 | modelBuilder.Entity("vega.Core.Models.Photo", b =>
134 | {
135 | b.HasOne("vega.Core.Models.Vehicle")
136 | .WithMany("Photos")
137 | .HasForeignKey("VehicleId");
138 | });
139 |
140 | modelBuilder.Entity("vega.Core.Models.Vehicle", b =>
141 | {
142 | b.HasOne("vega.Core.Models.Model", "Model")
143 | .WithMany()
144 | .HasForeignKey("ModelId")
145 | .OnDelete(DeleteBehavior.Cascade);
146 | });
147 |
148 | modelBuilder.Entity("vega.Core.Models.VehicleFeature", b =>
149 | {
150 | b.HasOne("vega.Core.Models.Feature", "Feature")
151 | .WithMany()
152 | .HasForeignKey("FeatureId")
153 | .OnDelete(DeleteBehavior.Cascade);
154 |
155 | b.HasOne("vega.Core.Models.Vehicle", "Vehicle")
156 | .WithMany("Features")
157 | .HasForeignKey("VehicleId")
158 | .OnDelete(DeleteBehavior.Cascade);
159 | });
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/Persistence/PhotoRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using Microsoft.EntityFrameworkCore;
5 | using vega.Core;
6 | using vega.Core.Models;
7 |
8 | namespace vega.Persistence
9 | {
10 | public class PhotoRepository : IPhotoRepository
11 | {
12 | private readonly VegaDbContext context;
13 | public PhotoRepository(VegaDbContext context)
14 | {
15 | this.context = context;
16 | }
17 | public async Task> GetPhotos(int vehicleId)
18 | {
19 | return await context.Photos
20 | .Where(p => p.VehicleId == vehicleId)
21 | .ToListAsync();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Persistence/UnitOfWork.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using vega.Core;
3 |
4 | namespace vega.Persistence
5 | {
6 | public class UnitOfWork : IUnitOfWork
7 | {
8 | private readonly VegaDbContext context;
9 |
10 | public UnitOfWork(VegaDbContext context)
11 | {
12 | this.context = context;
13 | }
14 |
15 | public async Task CompleteAsync()
16 | {
17 | await context.SaveChangesAsync();
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Persistence/VegaDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using vega.Core.Models;
3 |
4 | namespace vega.Persistence
5 | {
6 | public class VegaDbContext : DbContext
7 | {
8 | public DbSet Vehicles { get; set; }
9 | public DbSet Makes { get; set; }
10 | public DbSet Models { get; set; }
11 | public DbSet Features { get; set; }
12 | public DbSet Photos { get; set; }
13 |
14 | public VegaDbContext(DbContextOptions options)
15 | : base(options)
16 | {
17 | }
18 |
19 | protected override void OnModelCreating(ModelBuilder modelBuilder)
20 | {
21 | modelBuilder.Entity().HasKey(vf =>
22 | new { vf.VehicleId, vf.FeatureId });
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Persistence/VehicleRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Linq.Expressions;
5 | using System.Threading.Tasks;
6 | using Microsoft.EntityFrameworkCore;
7 | using vega.Core;
8 | using vega.Core.Models;
9 | using vega.Extensions;
10 |
11 | namespace vega.Persistence
12 | {
13 | public class VehicleRepository : IVehicleRepository
14 | {
15 | private readonly VegaDbContext context;
16 |
17 | public VehicleRepository(VegaDbContext context)
18 | {
19 | this.context = context;
20 | }
21 |
22 | public async Task GetVehicle(int id, bool includeRelated = true)
23 | {
24 | if (!includeRelated)
25 | return await context.Vehicles.FindAsync(id);
26 |
27 | return await context.Vehicles
28 | .Include(v => v.Features)
29 | .ThenInclude(vf => vf.Feature)
30 | .Include(v => v.Model)
31 | .ThenInclude(m => m.Make)
32 | .SingleOrDefaultAsync(v => v.Id == id);
33 | }
34 |
35 | public void Add(Vehicle vehicle)
36 | {
37 | context.Vehicles.Add(vehicle);
38 | }
39 |
40 | public void Remove(Vehicle vehicle)
41 | {
42 | context.Remove(vehicle);
43 | }
44 |
45 | public async Task> GetVehicles(VehicleQuery queryObj)
46 | {
47 | var result = new QueryResult();
48 |
49 | var query = context.Vehicles
50 | .Include(v => v.Model)
51 | .ThenInclude(m => m.Make)
52 | .AsQueryable();
53 |
54 | query = query.ApplyFiltering(queryObj);
55 |
56 | var columnsMap = new Dictionary>>()
57 | {
58 | ["make"] = v => v.Model.Make.Name,
59 | ["model"] = v => v.Model.Name,
60 | ["contactName"] = v => v.ContactName
61 | };
62 | query = query.ApplyOrdering(queryObj, columnsMap);
63 |
64 | result.TotalItems = await query.CountAsync();
65 |
66 | query = query.ApplyPaging(queryObj);
67 |
68 | result.Items = await query.ToListAsync();
69 |
70 | return result;
71 | }
72 |
73 | }
74 | }
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Hosting;
7 |
8 | namespace Vega
9 | {
10 | public class Program
11 | {
12 | public static void Main(string[] args)
13 | {
14 | var host = new WebHostBuilder()
15 | .UseKestrel()
16 | .UseContentRoot(Directory.GetCurrentDirectory())
17 | .UseIISIntegration()
18 | .UseStartup()
19 | .Build();
20 |
21 | host.Run();
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vega
2 | A sample vehicle dealer app built with Angular 2, ASP.NET Core and Entity Framework Core. This is part of my Udemy course: "Build a Real-world App with ASP.NET Core and Angular 2".
3 |
4 | # To run the project:
5 | ```
6 | $ npm install
7 | $ dotnet restore
8 | $ dotnet user-secrets set ConnectionStrings:Default ""
9 | $ webpack --config webpack.config.vendor.js
10 | $ webpack
11 | $ dotnet ef database update
12 | $ dotnet watch run
13 | ```
14 |
--------------------------------------------------------------------------------
/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.AspNetCore.SpaServices.Webpack;
5 | using Microsoft.Extensions.Configuration;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Logging;
8 | using vega.Persistence;
9 | using vega.Core;
10 | using AutoMapper;
11 | using vega.Core.Models;
12 | using vega.Controllers;
13 |
14 | namespace Vega
15 | {
16 | public class Startup
17 | {
18 | public Startup(IHostingEnvironment env)
19 | {
20 | var builder = new ConfigurationBuilder()
21 | .SetBasePath(env.ContentRootPath)
22 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
23 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
24 |
25 | if (env.IsDevelopment())
26 | builder = builder.AddUserSecrets();
27 |
28 | builder = builder.AddEnvironmentVariables();
29 | Configuration = builder.Build();
30 | }
31 |
32 | public IConfigurationRoot Configuration { get; }
33 |
34 | // This method gets called by the runtime. Use this method to add services to the container.
35 | public void ConfigureServices(IServiceCollection services)
36 | {
37 | services.Configure(Configuration.GetSection("PhotoSettings"));
38 |
39 | services.AddScoped();
40 | services.AddScoped();
41 | services.AddScoped();
42 | services.AddTransient();
43 | services.AddTransient();
44 |
45 | services.AddAutoMapper();
46 |
47 | services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("Default")));
48 |
49 | services.AddAuthorization(options => {
50 | options.AddPolicy(Policies.RequireAdminRole, policy => policy.RequireClaim("https://vega.com/roles", "Admin"));
51 | });
52 |
53 | // Add framework services.
54 | services.AddMvc();
55 | }
56 |
57 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
58 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
59 | {
60 | loggerFactory.AddConsole(Configuration.GetSection("Logging"));
61 | loggerFactory.AddDebug();
62 |
63 | if (env.IsDevelopment())
64 | {
65 | app.UseDeveloperExceptionPage();
66 | app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
67 | HotModuleReplacement = true
68 | });
69 | }
70 | else
71 | {
72 | app.UseExceptionHandler("/Home/Error");
73 | }
74 |
75 | app.UseStaticFiles();
76 |
77 | var options = new JwtBearerOptions
78 | {
79 | Audience = "https://api.vega.com",
80 | Authority = "https://vegaproject.auth0.com/"
81 | };
82 | app.UseJwtBearerAuthentication(options);
83 |
84 | app.UseMvc(routes =>
85 | {
86 | routes.MapRoute(
87 | name: "default",
88 | template: "{controller=Home}/{action=Index}/{id?}");
89 |
90 | routes.MapSpaFallbackRoute(
91 | name: "spa-fallback",
92 | defaults: new { controller = "Home", action = "Index" });
93 | });
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Vega.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vega-project
9 | netcoreapp1.1
10 | true
11 | false
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 | %(DistFiles.Identity)
42 | PreserveNewest
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Home Page";
3 | }
4 |
5 | Loading...
6 |
7 |
8 | @section scripts {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | ViewData["Title"] = "Error";
3 | }
4 |
5 | Error.
6 | An error occurred while processing your request.
7 |
--------------------------------------------------------------------------------
/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | @ViewData["Title"] - Vega
7 |
8 |
9 |
10 |
11 | @RenderBody()
12 |
13 | @RenderSection("scripts", required: false)
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using Vega
2 | @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
3 | @addTagHelper "*, Microsoft.AspNetCore.SpaServices"
4 |
--------------------------------------------------------------------------------
/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "_Layout";
3 | }
4 |
--------------------------------------------------------------------------------
/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "PhotoSettings": {
3 | "MaxBytes": 10485760,
4 | "AcceptedFileTypes": [".jpg", ".jpeg", ".png"]
5 | },
6 | "Logging": {
7 | "IncludeScopes": false,
8 | "LogLevel": {
9 | "Default": "Debug",
10 | "System": "Information",
11 | "Microsoft": "Information"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": { "version": "1.0.0-preview3-004056" }
3 | }
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Vega",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "@angular/common": "^2.4.5",
6 | "@angular/compiler": "^2.4.5",
7 | "@angular/core": "^2.4.5",
8 | "@angular/forms": "^2.4.5",
9 | "@angular/http": "^2.4.5",
10 | "@angular/platform-browser": "^2.4.5",
11 | "@angular/platform-browser-dynamic": "^2.4.5",
12 | "@angular/platform-server": "^2.4.5",
13 | "@angular/router": "^3.4.5",
14 | "@types/node": "^6.0.42",
15 | "angular2-chartjs": "^0.2.0",
16 | "angular2-jwt": "^0.2.2",
17 | "angular2-platform-node": "~2.0.11",
18 | "angular2-template-loader": "^0.6.2",
19 | "angular2-universal": "^2.1.0-rc.1",
20 | "angular2-universal-patch": "^0.2.1",
21 | "angular2-universal-polyfills": "^2.1.0-rc.1",
22 | "aspnet-prerendering": "^2.0.0",
23 | "aspnet-webpack": "^1.0.17",
24 | "auth0-lock": "^10.15.0",
25 | "awesome-typescript-loader": "^3.0.0",
26 | "bootstrap": "^3.3.7",
27 | "chart.js": "^2.5.0",
28 | "css": "^2.2.1",
29 | "css-loader": "^0.25.0",
30 | "es6-shim": "^0.35.1",
31 | "event-source-polyfill": "^0.0.7",
32 | "expose-loader": "^0.7.1",
33 | "extract-text-webpack-plugin": "^2.0.0-rc",
34 | "file-loader": "^0.9.0",
35 | "font-awesome": "^4.7.0",
36 | "html-loader": "^0.4.4",
37 | "isomorphic-fetch": "^2.2.1",
38 | "jquery": "^2.2.1",
39 | "json-loader": "^0.5.4",
40 | "ng2-toasty": "^2.5.0",
41 | "preboot": "^4.5.2",
42 | "raven-js": "^3.14.0",
43 | "raw-loader": "^0.5.1",
44 | "rxjs": "^5.3.0",
45 | "style-loader": "^0.13.1",
46 | "to-string-loader": "^1.1.5",
47 | "typescript": "^2.2.1",
48 | "underscore": "^1.8.3",
49 | "url-loader": "^0.5.7",
50 | "webpack": "^2.2.0",
51 | "webpack-hot-middleware": "^2.12.2",
52 | "webpack-merge": "^0.14.1",
53 | "zone.js": "^0.7.8"
54 | },
55 | "devDependencies": {
56 | "@angular/cli": "^1.0.0-rc.4",
57 | "webpack": "^2.6.0"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "target": "es5",
5 | "sourceMap": true,
6 | "experimentalDecorators": true,
7 | "emitDecoratorMetadata": true,
8 | "skipDefaultLibCheck": true,
9 | "lib": [ "es6", "dom" ],
10 | "types": [ "node" ]
11 | },
12 | "exclude": [ "bin", "node_modules" ],
13 | "atom": { "rewriteTsconfig": false }
14 | }
15 |
--------------------------------------------------------------------------------
/web.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const merge = require('webpack-merge');
4 | const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
5 |
6 | module.exports = (env) => {
7 | // Configuration in common to both client-side and server-side bundles
8 | const isDevBuild = !(env && env.prod);
9 | const sharedConfig = {
10 | stats: { modules: false },
11 | context: __dirname,
12 | resolve: { extensions: [ '.js', '.ts' ] },
13 | output: {
14 | filename: '[name].js',
15 | publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix
16 | },
17 | module: {
18 | rules: [
19 | { test: /\.ts$/, include: /ClientApp/, use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] },
20 | { test: /\.html$/, use: 'html-loader?minimize=false' },
21 | { test: /\.css$/, use: ['to-string-loader', 'css-loader'] },
22 | { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
23 | ]
24 | },
25 | plugins: [new CheckerPlugin()]
26 | };
27 |
28 | // Configuration for client-side bundle suitable for running in browsers
29 | const clientBundleOutputDir = './wwwroot/dist';
30 | const clientBundleConfig = merge(sharedConfig, {
31 | entry: { 'main-client': './ClientApp/boot-client.ts' },
32 | output: { path: path.join(__dirname, clientBundleOutputDir) },
33 | plugins: [
34 | new webpack.DllReferencePlugin({
35 | context: __dirname,
36 | manifest: require('./wwwroot/dist/vendor-manifest.json')
37 | })
38 | ].concat(isDevBuild ? [
39 | // Plugins that apply in development builds only
40 | new webpack.SourceMapDevToolPlugin({
41 | filename: '[file].map', // Remove this line if you prefer inline source maps
42 | moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
43 | })
44 | ] : [
45 | // Plugins that apply in production builds only
46 | new webpack.optimize.UglifyJsPlugin()
47 | ])
48 | });
49 |
50 | // Configuration for server-side (prerendering) bundle suitable for running in Node
51 | const serverBundleConfig = merge(sharedConfig, {
52 | resolve: { mainFields: ['main'] },
53 | entry: { 'main-server': './ClientApp/boot-server.ts' },
54 | plugins: [
55 | new webpack.DllReferencePlugin({
56 | context: __dirname,
57 | manifest: require('./ClientApp/dist/vendor-manifest.json'),
58 | sourceType: 'commonjs2',
59 | name: './vendor'
60 | })
61 | ],
62 | output: {
63 | libraryTarget: 'commonjs',
64 | path: path.join(__dirname, './ClientApp/dist')
65 | },
66 | target: 'node',
67 | devtool: 'inline-source-map'
68 | });
69 |
70 | return [clientBundleConfig, serverBundleConfig];
71 | };
72 |
--------------------------------------------------------------------------------
/webpack.config.vendor.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
4 | const merge = require('webpack-merge');
5 |
6 | module.exports = (env) => {
7 | const extractCSS = new ExtractTextPlugin('vendor.css');
8 | const isDevBuild = !(env && env.prod);
9 | const sharedConfig = {
10 | stats: { modules: false },
11 | resolve: { extensions: [ '.js' ] },
12 | module: {
13 | rules: [
14 | { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
15 | ]
16 | },
17 | entry: {
18 | vendor: [
19 | '@angular/common',
20 | '@angular/compiler',
21 | '@angular/core',
22 | '@angular/http',
23 | '@angular/platform-browser',
24 | '@angular/platform-browser-dynamic',
25 | '@angular/router',
26 | '@angular/platform-server',
27 | 'angular2-chartjs',
28 | 'angular2-jwt',
29 | 'angular2-universal',
30 | 'angular2-universal-polyfills',
31 | 'auth0-lock',
32 | 'bootstrap',
33 | 'bootstrap/dist/css/bootstrap.css',
34 | 'chart.js',
35 | 'es6-shim',
36 | 'es6-promise',
37 | 'event-source-polyfill',
38 | 'font-awesome/css/font-awesome.css',
39 | 'ng2-toasty',
40 | 'ng2-toasty/bundles/style-bootstrap.css',
41 | 'jquery',
42 | 'raven-js',
43 | 'underscore',
44 | 'zone.js',
45 | ]
46 | },
47 | output: {
48 | publicPath: '/dist/',
49 | filename: '[name].js',
50 | library: '[name]_[hash]'
51 | },
52 | plugins: [
53 | new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable)
54 | new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580
55 | new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100
56 | ]
57 | };
58 |
59 | const clientBundleConfig = merge(sharedConfig, {
60 | output: { path: path.join(__dirname, 'wwwroot', 'dist') },
61 | module: {
62 | rules: [
63 | { test: /\.css(\?|$)/, use: extractCSS.extract({ use: 'css-loader' }) }
64 | ]
65 | },
66 | plugins: [
67 | extractCSS,
68 | new webpack.DllPlugin({
69 | path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'),
70 | name: '[name]_[hash]'
71 | })
72 | ].concat(isDevBuild ? [] : [
73 | new webpack.optimize.UglifyJsPlugin()
74 | ])
75 | });
76 |
77 | const serverBundleConfig = merge(sharedConfig, {
78 | target: 'node',
79 | resolve: { mainFields: ['main'] },
80 | output: {
81 | path: path.join(__dirname, 'ClientApp', 'dist'),
82 | libraryTarget: 'commonjs2',
83 | },
84 | module: {
85 | rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', 'css-loader'] } ]
86 | },
87 | entry: { vendor: ['aspnet-prerendering'] },
88 | plugins: [
89 | new webpack.DllPlugin({
90 | path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
91 | name: '[name]_[hash]'
92 | })
93 | ]
94 | });
95 |
96 | return [clientBundleConfig, serverBundleConfig];
97 | }
98 |
--------------------------------------------------------------------------------
/wwwroot/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mosh-hamedani/vega/4a09f50a4181d60134a9a816370b52276381c4d9/wwwroot/.DS_Store
--------------------------------------------------------------------------------
/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
1 | h F � � 00 �% V @@ (B �: ( @ ��у��̅��˅��˅��ʅ��ʅ��Ʌ�������������������������������۶�����������������������������������������������������������������������۳u�НL�Ыn�����������������������������������������������������ܵx�ҢV�Щm�����������������������������������������������������ܶ{�ӣX�Ъn������������������������������������������������������ե\�Ѭq������������������������������������������������������רa�Ӯu�����������������������������������������������������Ệ�ةd�ԯx������������������������������������������������S����⽉�ګi�հ{������������������������������������������������E����㾌�ܯq�ص�������������������������������������������������E����侎�ܭo�ײ�������������������������������������������������E����俏�ݮq�س�������������������������������������������������E����忑�ޯu�ܻ�������������������������������������������������C�����ϭ������ġ�������������������������������������������ҟ�����������������������������������������������������������������������������A����������������������������������ן��������� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� ��( @ � ((()������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ʠ��ˡ��ʞ��ɞ��л����������������������������������������������������������������������������������������������������������ˢ�ΘC�ϛG�͙A�͙B�ϴ�����������������������������������������������������������������������������������������������������������̥�ЛJ�џP�ӤX�ӤX�Ѻ�����������������������������������������������������������������������������������������������������������ͥ�ϛI�Ԧ\�ўN�НK�ж�����������������������������������������������������������������������������������������������������������Χ�ѝL�է^�ўN�МL�ж�����������������������������������������������������������������������������������������������������������Ϩ�ўN�ը`�џP�НM�з�����������������������������������������������������������������������������������������������������������ϩ�ѝM�֨`�џP�НM�ж�����������������������������������������������������������������������������������������������������������ѫ�ӡU�f�ӢV�ҠS�Ѹ�����������������������������������������������������������������������������������������������������������Ѭ�ҟQ�שc�ӠS�ҟP�Ѹ�����������������������������������������������������������������������������������������������������������Ү�գY�٭j�եZ�ԣX�Һ�����������������������������������������������������������������������������������������������������������Ү�բW�٬i�դY�ԢW�ҹ�����������������������������������������������������������������������������������������������������������Ӱ�֤\�ڮm�צ^�դ[�һ����������������������������������������������������������������������������������������������...#���������ӱ�֣[�ڭk�֤\�գY�Ӻ���������������������������������������������������������������������������������������������� ���������ճ�٧b�ܰr�٧c�ئ`�Ի���������������������������������������������������������������������������������������������� ���������Բ�פ^�ۮo�إ`�פ]�Ӻ���������������������������������������������������������������������������������������������� ���������յ�ڨe�w�y�w��Ĩ��������������������������������������������������������������������������������������������� ���������Դ�٦a�ݯr�٦a�إ_�Ժ���������������������������������������������������������������������������������������������� ���������ն�۩g�w�۪i�کg�ս���������������������������������������������������������������������������������������������� ���������յ�ڧd�ޱu�کf�ڧd�ռ���������������������������������������������������������������������������������������������� ���������շ�ۨg�w�۪j�۩g�ս���������������������������������������������������������������������������������������������� ���������ַ�۩i�߲y�ܫk�۪i�ս�������������������������������������������������������������������������������������������� ���������շ�ۨi�߲y�ݬn�ܫm��¥�������������������������������������������������������������������������������������������Ç ���������ָ�ܪl�ޮr�߲y���~��˵��������������������������������������������������������������������������������������������������������ַ�ܨi�ݫl�ܩi�ܩj�ս����������������������������������������������������������������������������������������ۿ�������������������շ��ָ��ַ��ָ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ܿ��������������������� ( 0 ` �% ��� ihhi��������������������������������~��~��~��~}��~}���������������~��~��~��~��~��~��~�JIH_��ԁ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������zwv����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������۵w�۳u�۴u�۴v�ڳs�ڲs�ڳu��̼�����������������������������������������������������������������������������������������������������������������������������������������������������������������ϛH�ΘC�ΙD�ϛF�͘@�͘@�ΚE�������������������������������������������������������������������������������������������������������������������������������������������������������������������ЛI�ΙD�ΚE�ΚF�͗@�͗@�ΙD��¬�����������������������������������������������������������������������������������������������������������������������������������������������������������������џP�МL�ўO�֩c�֪c�ժc�֫e��Ƶ�����������������������������������������������������������������������������������������������������������������������������������������������������������������ѝM�ϛI�џP�f�ўN�ОL�џP��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ѝL�ϛH�ўO�ըa�ϚG�ΙE�ϜJ��®�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠQ�ўN�ҢT�f�ўM�НL�ўO��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠS�ўO�ҢU�g�ўN�НL�џP��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҟP�ѝL�ҡR�֪d�НK�ϛI�ѝN��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ҠR�ѝM�ӢU�f�ОM�НK�ўO��ï�����������������������������������������������������������������������������������������������������������������������������������������������������������������ԤZ�ӢW�զ]�ٯm�ӡU�ҠS�ӢW��ı�����������������������������������������������������������������������������������������������������������������������������������������������������������������ӢV�ҟQ�ӣW�جh�ўP�ўN�ҟQ��ð���������������������������������������������������������������������������������������������������������������������������������������������{zz�����������������ԢV�ҟQ�ԢX�٬i�ҟP�ўO�ҠR��İ���������������������������������������������������������������������������������������������������������������������������������������������~}�����������������֥^�դZ�֨`�ڱq�դY�ԣX�ԤZ��Ų�����������������������������������������������������������������������������������������������������������������������������������������������������������������֣Z�աV�֥\�گm�ӢU�ӠT�ԣW��ı�����������������������������������������������������������������������������������������������������������������������������������������������������������������֤\�բX�צ^�ڰp�ԣX�ԢV�գZ��IJ�����������������������������������������������������������������������������������������������������������������������������������������������������������������اa�֥]�بd�ܲt�ץ]�դ[�է^��ų�����������������������������������������������������������������������������������������������������������������������������������������999K$$$)����������������ץ^�֢Y�צ_�ۯp�բX�ԡV�դZ��IJ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ץ_�֣[�ק`�ܰq�գY�բW�֤[��Ų����������������������������������������������������������������������������������������������������������������������������������������� ����������������کf�٨c�ګi�x�ئa�ئ`�بc��ƴ����������������������������������������������������������������������������������������������������������������������������������������� ����������������٧b�إ_�٩e�ݱu�פ^�פ\�צ`��ų�����������������������������������������������������������������������������������������������������������������������������������������
����������������٧b�פ^�بe�ݱu�ף]�֣\�פ^��IJ����������������������������������������������������������������������������������������������������������������������������������������� ����������������کg�٧c�۫i�ก�ܮp�ܭn�y��ʼ����������������������������������������������������������������������������������������������������������������������������������������� ����������������کh�ڨe�۫k�ข�ܯq�ۮp�ܯr��Ǹ�����������������������������������������������������������������������������������������������������������������������������������������
����������������ڨe�٦b�کh�w�٥`�ؤ_�٦b��Ŵ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۩h�ڨe�۫k�ߴz�ڧd�٧c�کf��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۪k�۩h�ܭn���~�کh�کf�۫i��ƶ�����������������������������������������������������������������������������������������������������������������������������������������
����������������ڨh�ڧd�۫k�ߴz�ڧd�٦c�کf��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������۩j�ۨf�۫l�ߵ|�ڨf�ڧd�۪h��Ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ܬn�ܪk�ݮq�ᶀ�۪k�۪j�ܬl��Ƕ�����������������������������������������������������������������������������������������������������������������������������������������
����������������ܪk�ۨh�ܬn���}�۩g�ڨf�۪j��ƶ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ܪl�ۨh�ܬn���~�ܩi�ۨh�ܬn��Ƿ����������������������������������������������������������������������������������������������������������������������������������������� ����������������ݬp�ܫm�ޮr�⺇��������ƛ��������������������������������������������������������������������������������������������������������������������������������������������� �������������������ݫm�ܩj�ܪl�ݮr�ݬn�ܭo�����ʽ���������������������������������������������������������������������������������������������������������������������������������������������������������������ܪl�ܨi�ܪj�ݫm�ܨi�ۨi�ܪk��ƶ��������������������������������������������������������������������������������������������������������������������������������������������������������������ݫo�ܪl�ݫm�ݬp�ܪl�ܪl�ݬo��Ƿ������������������������������������������������������������������������������������������������������������������������������������!����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!��������������������������������������������������������Ͽ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������п������������������������������������������������������������������������������������������������������߿���!��������������������������������������������������������������ֿ�������������������������������������������������������������������������������������������������������!������������������������������ �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� ��( @ � B ������ +>>>u===w===u===u===u===u===u===u===u<<>>q>>>q>>>q>>>q>>>q>>>q===q===q===q===q===q===q===q===q???s9��� <<>>��������������������ОM�ΘB�ΘC�ΘA�НK�ΙB�͘?�͗>�ΛF�ΙD�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ўN�ϙE�ϚE�ΙD�ОL�ΚD�ΙB�͘@�ϛG�ΚE�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������џN�ϙD�ϙE�ΙD�ϜJ�͗A�͖>�̕=�ΙD�͘A�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҡV�џP�ўO�МK�حj�ڲr�ٱo�ٰn�ٳs�ڲp�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠR�МJ�ЛJ�ΙD�۵x�ԤY�џN�џM�ҢT�ҠR�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠQ�ЛH�ϛH�ΘC�ڲs�џO�ΘD�ΘC�МJ�ϛH�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ҠR�ЛH�МI�ΘD�ڲs�ѠP�ϙE�ΘE�НL�ϜJ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ԣW�ҟP�ҟP�НK�ܶy�ӣW�ўM�ўM�ҠR�џP�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>>>��������������������ԣX�ҟQ�ҟQ�НL�ܶy�ӤX�ўM�ОM�ҠR�џQ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ӡU�ѝK�ѝL�ЛF�۴u�ҡS�ЛH�ϛG�ўN�ѝL�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ԣW�ўM�ѝM�МH�۴v�ӢU�НJ�ЛI�џP�ѝN�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ԣW�ўN�ҞN�ќJ�ܴx�ӢV�НK�НJ�ҟQ�ўO�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������֧^�գY�ԤZ�ӡU���֨_�ӡU�ӡT�ԤZ�ԣX�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������դZ�ҟQ�ҟQ�ѝK�ܶy�ԢW�ўM�ѝL�ҠR�ҟP�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������ե[�ӠR�ҠR�ўN�z�գY�ѝO�ўN�ӡS�ҠR�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������???��������������������֥[�ԟQ�ҠR�ќM�z�դY�ҞN�ѝN�ӡS�ӡQ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@��������������������רb�֥\�֥\�ԣW���שb�գX�ԣX�զ]�եZ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@@@��������������������צ_�բW�բW�ԠR��ק^�ԡS�ԠS�ԣY�գW�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������צ^�աU�աU�ӟP�~�֦\�ҠR�ӠR�գX�ԣV�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������اa�֣Z�գZ�աU�߹��רa�բW�բV�֥\�֣Z�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������BBB��������������������ڪf�ק`�צ`�֤\�༆�٫g�צ]�֥]�֨a�֨`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������999a>>>]'��������������������اa�֢X�עX�՟T�߹��ا`�ՠU�ԠT�֤[�դY����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������بd�֣[�֣Z�աV�แ�اa�բW�բV�֥\�֤[����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������٨d�ף[�֣\�աV�ṁ�קa�բW�բV�֥\�֤[����������������������������������������������������������������������������������������������������������������������������������������������Ŀ����������������������������������������� �����������������������۬j�ڨd�کe�ئ`�⽉�۬j�ئ`�ئ`�٩e�٩c����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪g�٦`�٦`�ؤ\�ễ�کg�פ\�פ]�٧a�צ`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ڪf�إ^�פ^�֢Z�ễ�٨e�ף[�֣Z�ئa�ץ^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪g�إ_�ؤ_�֢[�⻅�٨e�ע[�֢[�٦a�֣Z����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫i�٦b�٧b�ؤ]�㽉�ܯp�ڨf�کf�۫j������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ �����������������������ܭn�۫i�۪i�ڧc���⾌�ễ�ẃ�⼉�ṃ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۪i�٥a�٦a�أ]�⺆�ڨe�עZ�֢Z�إ`�פ^����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫j�ڦb�ڧc�ؤ^�⼈�۫i�٥_�٤_�کe�٦c����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������۫k�ڦc�ڧc�٤_�㼈�۫i�٤`�ئ`�ڨf�٨d����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݮq�ܫl�ܬl�۪h�����ݰs�۫j�۫i�ܭn�ܭl����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫl�ڧd�ڧe�٥`�㽊�ܬk�٦b�٦b�۩g�کf����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫm�ڧe�ڧe�٥a�㽊�ܬl�ڦb�٦b�۩h�کf����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ܫm�ۧe�ۨe�٥a�㽊�ܬl�ڦc�ڦb�۪h�۩f����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ޭq�ܪj�ܪk�ۨf�俎�ݮq�۩h�۩h�ܬm�ܫk����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݮr�ܫl�ܫl�۩h�忐�ޯs�۪j�۪j�ܭn�ܭm����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݬn�ۧf�ۨg�ڦc�佋�ݬn�ڧd�ڧd�۪j�۪i����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݬo�ۨh�ۨh�ڦd�佌�ݬn�ۧe�ۧe�۩i�ڧd����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �����������������������ݭq�ܩj�ܪj�ۧf�����߲w�ݭo�ݫm���{��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������������������������߯u�ެp�ݭp�ݫm�㻈�忐�位�㼉��ɡ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܨh�ܩh�ܨh�ܫn�ۧf�ۦd�ڦc�ܪk�ްt����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܨi�ܩj�ܩi�ޭq�ܪk�ܨi�ۨh�ݫm�۪j���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ݭp�ܧh�ܨi�ܩh�ޭq�ܪj�ܨh�ۨg�ݬm�ܫl��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ޯt�ݬp�ݭp�ݭp�߯u�ݭq�ޭp�ݬo�ޮs�ޮr��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
--------------------------------------------------------------------------------