diff --git a/src/app/app.module.ts b/src/app/app.module.ts index eddf8dcd..eaa580ca 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -44,6 +44,10 @@ const appRoutes: Routes = [ path : 'apps/e-commerce', loadChildren: './main/content/apps/e-commerce/e-commerce.module#FuseEcommerceModule' }, + { + path : 'apps/academy', + loadChildren: './main/content/apps/academy/academy.module#FuseAcademyModule' + }, { path : 'apps/todo', loadChildren: './main/content/apps/todo/todo.module#FuseTodoModule' diff --git a/src/app/fuse-fake-db/academy.ts b/src/app/fuse-fake-db/academy.ts new file mode 100644 index 00000000..06e48d34 --- /dev/null +++ b/src/app/fuse-fake-db/academy.ts @@ -0,0 +1,421 @@ +export class AcademyFakeDb +{ + public static courses = [ + { + 'id' : '15459251a6d6b397565', + 'title' : 'Basics of Angular', + 'slug' : 'basics-of-angular', + 'category' : 'web', + 'length' : 30, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '154588a0864d2881124', + 'title' : 'Basics of TypeScript', + 'slug' : 'basics-of-typeScript', + 'category' : 'web', + 'length' : 60, + 'updated' : 'Nov 01, 2017' + }, + { + 'id' : '15453ba60d3baa5daaf', + 'title' : 'Android N: Quick Settings', + 'slug' : 'android-n-quick-settings', + 'category' : 'android', + 'length' : 120, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '15453a06c08fb021776', + 'title' : 'Keep Sensitive Data Safe and Private', + 'slug' : 'keep-sensitive-data-safe-and-private', + 'category' : 'android', + 'length' : 45, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '15427f4c1b7f3953234', + 'title' : 'Building a gRPC Service with Java', + 'slug' : 'building-a-grpc-service-with-java', + 'category' : 'cloud', + 'length' : 30, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1542d75d929a603125', + 'title' : 'Build a PWA Using Workbox', + 'slug' : 'build-a-pwa-using-workbox', + 'category' : 'web', + 'length' : 120, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1543ee3a5b43e0f9f45', + 'title' : 'Build an App for the Google Assistant with Firebase and Dialogflow', + 'slug' : 'build-an-app-for-the-google-assistant-with-firebase-and-dialogflow', + 'category' : 'firebase', + 'length' : 30, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1543cc4515df3146112', + 'title' : 'Cloud Functions for Firebase', + 'slug' : 'cloud-functions-for-firebase', + 'category' : 'firebase', + 'length' : 45, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '154398a4770d7aaf9a2', + 'title' : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge', + 'slug' : 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge', + 'category' : 'cloud', + 'length' : 90, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '15438351f87dcd68567', + 'title' : 'Building Beautiful UIs with Flutter', + 'your' : 'building-beautiful-uis-with-flutter', + 'category' : 'web', + 'length' : 90, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1544e43dcdae6ebf876', + 'title' : 'Cloud Firestore', + 'slug' : 'cloud-firestore', + 'category' : 'firebase', + 'length' : 90, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1541ca7af66da284177', + 'title' : 'Customize Network Topology with Subnetworks', + 'slug' : 'customize-network-topology-with-subnetworks', + 'category' : 'web', + 'length' : 45, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '154297167e781781745', + 'title' : 'Looking at Campaign Finance with BigQuery', + 'slug' : 'looking-at-campaign-finance-with-bigquery', + 'category' : 'cloud', + 'length' : 60, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '154537435d5b32bf11a', + 'title' : 'Firebase Android', + 'slug' : 'firebase-android', + 'category' : 'android', + 'length' : 45, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '154204e45a59b168453', + 'title' : 'Simulating a Thread Network Using OpenThread', + 'slug' : 'simulating-a-thread-network-using-openthread', + 'category' : 'web', + 'length' : 45, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1541dd1e05dfc439216', + 'title' : 'Your First Progressive Web App', + 'slug' : 'your-first-progressive-web-app', + 'category' : 'web', + 'length' : 30, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1532dfc67e704e48515', + 'title' : 'Launch Cloud Datalab', + 'slug' : 'launch-cloud-datalab', + 'category' : 'cloud', + 'length' : 60, + 'updated' : 'Jun 28, 2017' + }, + { + 'id' : '1542e43dfaae6ebf226', + 'title' : 'Personalize Your iOS App with Firebase User Management', + 'slug' : 'personalize-your-ios-app-with-firebase-user-management', + 'category' : 'firebase', + 'length' : 90, + 'updated' : 'Jun 28, 2017' + } + ]; + + public static categories = [ + { + 'id' : 0, + 'value': 'web', + 'label': 'Web' + }, + { + 'id' : 1, + 'value': 'firebase', + 'label': 'Firebase' + }, + { + 'id' : 2, + 'value': 'cloud', + 'label': 'Cloud' + }, + { + 'id' : 3, + 'value': 'android', + 'label': 'Android' + } + ]; + + private static demoSteps = [ + { + 'title' : 'Introduction', + 'content': '1 Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Get the sample code', + 'content': '2 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Create a Firebase project and Set up your app', + 'content': '3 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Install the Firebase Command Line Interface', + 'content': '4 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Deploy and run the web app', + 'content': '5 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'The Functions Directory', + 'content': '6 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Import the Cloud Functions and Firebase Admin modules', + 'content': '7 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Welcome New Users', + 'content': '8 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Images moderation', + 'content': '9 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'New Message Notifications', + 'content': '10 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + }, + { + 'title' : 'Congratulations!', + 'content': '11 Lorem ipsum dolor sit amet, consectetur adipiscing elit' + } + ]; + + public static course = [ + { + 'id' : '15459251a6d6b397565', + 'title' : 'Basics of Angular', + 'slug' : 'basics-of-angular', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 30, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '154588a0864d2881124', + 'title' : 'Basics of TypeScript', + 'slug' : 'basics-of-typeScript', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 60, + 'totalSteps' : 11, + 'updated' : 'Nov 01, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '15453ba60d3baa5daaf', + 'title' : 'Android N: Quick Settings', + 'slug' : 'android-n-quick-settings', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'android', + 'length' : 120, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '15453a06c08fb021776', + 'title' : 'Keep Sensitive Data Safe and Private', + 'slug' : 'keep-sensitive-data-safe-and-private', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'android', + 'length' : 45, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '15427f4c1b7f3953234', + 'title' : 'Building a gRPC Service with Java', + 'slug' : 'building-a-grpc-service-with-java', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'cloud', + 'length' : 30, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1542d75d929a603125', + 'title' : 'Build a PWA Using Workbox', + 'slug' : 'build-a-pwa-using-workbox', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 120, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1543ee3a5b43e0f9f45', + 'title' : 'Build an App for the Google Assistant with Firebase and Dialogflow', + 'slug' : 'build-an-app-for-the-google-assistant-with-firebase-and-dialogflow', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'firebase', + 'length' : 30, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1543cc4515df3146112', + 'title' : 'Cloud Functions for Firebase', + 'slug' : 'cloud-functions-for-firebase', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'firebase', + 'length' : 45, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '154398a4770d7aaf9a2', + 'title' : 'Manage Your Pivotal Cloud Foundry App\'s Using Apigee Edge', + 'slug' : 'manage-your-pivotal-cloud-foundry-apps-using-apigee-Edge', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'cloud', + 'length' : 90, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '15438351f87dcd68567', + 'title' : 'Building Beautiful UIs with Flutter', + 'your' : 'building-beautiful-uis-with-flutter', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 90, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1544e43dcdae6ebf876', + 'title' : 'Cloud Firestore', + 'slug' : 'cloud-firestore', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'firebase', + 'length' : 90, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1541ca7af66da284177', + 'title' : 'Customize Network Topology with Subnetworks', + 'slug' : 'customize-network-topology-with-subnetworks', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 45, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '154297167e781781745', + 'title' : 'Looking at Campaign Finance with BigQuery', + 'slug' : 'looking-at-campaign-finance-with-bigquery', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'cloud', + 'length' : 60, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '154537435d5b32bf11a', + 'title' : 'Firebase Android', + 'slug' : 'firebase-android', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'android', + 'length' : 45, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '154204e45a59b168453', + 'title' : 'Simulating a Thread Network Using OpenThread', + 'slug' : 'simulating-a-thread-network-using-openthread', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 45, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1541dd1e05dfc439216', + 'title' : 'Your First Progressive Web App', + 'slug' : 'your-first-progressive-web-app', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'web', + 'length' : 30, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1532dfc67e704e48515', + 'title' : 'Launch Cloud Datalab', + 'slug' : 'launch-cloud-datalab', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'cloud', + 'length' : 60, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + }, + { + 'id' : '1542e43dfaae6ebf226', + 'title' : 'Personalize Your iOS App with Firebase User Management', + 'slug' : 'personalize-your-ios-app-with-firebase-user-management', + 'description': 'Commits that need to be pushed lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'category' : 'firebase', + 'length' : 90, + 'totalSteps' : 11, + 'updated' : 'Jun 28, 2017', + 'steps' : AcademyFakeDb.demoSteps + } + ]; + +} diff --git a/src/app/fuse-fake-db/fuse-fake-db.service.ts b/src/app/fuse-fake-db/fuse-fake-db.service.ts index 5d5a1cc7..41206e23 100644 --- a/src/app/fuse-fake-db/fuse-fake-db.service.ts +++ b/src/app/fuse-fake-db/fuse-fake-db.service.ts @@ -16,6 +16,7 @@ import { ScrumboardFakeDb } from './scrumboard'; import { FaqFakeDb } from './faq'; import { KnowledgeBaseFakeDb } from './knowledge-base'; import { ECommerceFakeDb } from './e-commerce'; +import { AcademyFakeDb } from './academy'; export class FuseFakeDbService implements InMemoryDbService { @@ -52,7 +53,10 @@ export class FuseFakeDbService implements InMemoryDbService 'knowledge-base' : KnowledgeBaseFakeDb.data, 'e-commerce-dashboard' : ECommerceFakeDb.dashboard, 'e-commerce-products' : ECommerceFakeDb.products, - 'e-commerce-orders' : ECommerceFakeDb.orders + 'e-commerce-orders' : ECommerceFakeDb.orders, + 'academy-categories' : AcademyFakeDb.categories, + 'academy-courses' : AcademyFakeDb.courses, + 'academy-course' : AcademyFakeDb.course }; } } diff --git a/src/app/main/content/apps/academy/academy.module.ts b/src/app/main/content/apps/academy/academy.module.ts new file mode 100644 index 00000000..20be6f3f --- /dev/null +++ b/src/app/main/content/apps/academy/academy.module.ts @@ -0,0 +1,47 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../../../../core/modules/shared.module'; +import { RouterModule } from '@angular/router'; + +import { FuseAcademyCoursesComponent } from './courses/courses.component'; +import { FuseAcademyCourseComponent } from './course/course.component'; +import { AcademyCoursesService } from './courses.service'; +import { AcademyCourseService } from './course.service'; + +const routes = [ + { + path : 'courses', + component: FuseAcademyCoursesComponent, + resolve : { + academy: AcademyCoursesService + } + }, + { + path : 'courses/:courseId/:courseSlug', + component: FuseAcademyCourseComponent, + resolve : { + academy: AcademyCourseService + } + }, + { + path : '**', + redirectTo: 'courses' + } +]; + +@NgModule({ + imports : [ + SharedModule, + RouterModule.forChild(routes) + ], + declarations: [ + FuseAcademyCoursesComponent, + FuseAcademyCourseComponent + ], + providers : [ + AcademyCoursesService, + AcademyCourseService + ] +}) +export class FuseAcademyModule +{ +} diff --git a/src/app/main/content/apps/academy/course.service.ts b/src/app/main/content/apps/academy/course.service.ts new file mode 100644 index 00000000..43b0be44 --- /dev/null +++ b/src/app/main/content/apps/academy/course.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { HttpClient } from '@angular/common/http'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class AcademyCourseService implements Resolve +{ + onCourseChanged: BehaviorSubject = new BehaviorSubject({}); + + constructor(private http: HttpClient) + { + } + + /** + * The Academy App Main Resolver + * + * @param {ActivatedRouteSnapshot} route + * @param {RouterStateSnapshot} state + * @returns {Observable | Promise | any} + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | any + { + return new Promise((resolve, reject) => { + + Promise.all([ + this.getCourse(route.params.courseId, route.params.courseSlug) + ]).then( + () => { + resolve(); + }, + reject + ); + }); + } + + getCourse(courseId, courseSlug): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/academy-course/' + courseId + '/' + courseSlug) + .subscribe((response: any) => { + this.onCourseChanged.next(response); + resolve(response); + }, reject); + }); + } + +} diff --git a/src/app/main/content/apps/academy/course/course.component.html b/src/app/main/content/apps/academy/course/course.component.html new file mode 100644 index 00000000..e892864c --- /dev/null +++ b/src/app/main/content/apps/academy/course/course.component.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + i, 'last': last, 'first': first}"> + + {{i + 1}} + + {{step.title}} + + + + + + + + + + + + + + + + + menu + + + + arrow_back + + + + {{course.title}} + + + + + + + + + + + + + + + + + + + + + + + + + chevron_left + + + + chevron_right + + + + check + + + + + + + + + + diff --git a/src/app/main/content/apps/academy/course/course.component.scss b/src/app/main/content/apps/academy/course/course.component.scss new file mode 100644 index 00000000..eed7b8f6 --- /dev/null +++ b/src/app/main/content/apps/academy/course/course.component.scss @@ -0,0 +1,202 @@ +@import "src/app/core/scss/fuse"; + +#academy-course { + + .mat-drawer-container { + flex: 1 !important; + + > .mat-drawer-content { + flex: 1 !important; + + @include media-breakpoint-up('lg') { + z-index: 52; + } + } + } + + .sidenav { + + .steps { + padding: 16px 0; + + .step { + position: relative; + display: flex; + justify-content: flex-start; + align-items: center; + padding: 16px; + cursor: pointer; + color: rgba(0, 0, 0, 0.54); + + &.current { + background: mat-color($mat-blue, 50); + color: rgba(0, 0, 0, 0.87); + + .index { + + span { + border: 2px solid mat-color($mat-blue, 500); + } + } + + .title { + font-weight: bold; + } + } + + &.completed { + color: rgba(0, 0, 0, 0.87); + + .index { + + span { + border: 2px solid mat-color($mat-blue, 500); + } + + &:after { + border-left-color: mat-color($mat-blue, 500); + } + } + + + .step { + + .index { + + &:before { + border-left-color: mat-color($mat-blue, 500); + } + } + } + } + + &.first { + + .index { + + &:before { + display: none; + } + } + } + + &.last { + + .index { + + &:after { + display: none; + } + } + } + + .index { + display: flex; + margin-right: 12px; + + span { + display: flex; + align-items: center; + justify-content: center; + position: relative; + width: 28px; + min-width: 28px; + max-width: 28px; + height: 28px; + background: white; + border-radius: 100%; + border: 2px solid mat-color($mat-grey, 500); + font-weight: bold; + font-size: 12px; + z-index: 10; + } + + &:before, + &:after { + position: absolute; + display: block; + content: ' '; + border-left: 1px solid mat-color($mat-grey, 300); + width: 1px; + height: 50%; + left: 29px; + z-index: 8; + } + + &:before { + top: 0; + } + + &:after { + bottom: 0; + } + } + + .title { + display: flex; + } + } + } + } + + .center { + position: relative; + + .header { + height: 72px; + min-height: 72px; + max-height: 72px; + } + + .content { + display: flex; + position: relative; + overflow: hidden; + height: 100%; + background: mat-color($mat-grey, 200); + + .course-step { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 48px; + + @media (max-width: 720px) { + padding: 0; + } + + .course-step-content { + padding: 24px; + max-width: 720px; + margin: 0 auto; + width: 100%; + background: white; + @include mat-elevation(4); + } + } + } + + .step-navigation { + position: absolute; + display: flex; + align-items: center; + justify-content: space-between; + bottom: 32px; + max-width: 944px; + padding: 0 24px; + width: 100%; + left: 50%; + transform: translateX(-50%); + + .previous { + margin-right: auto; + } + + .next, + .done { + margin-left: auto; + } + } + } +} diff --git a/src/app/main/content/apps/academy/course/course.component.ts b/src/app/main/content/apps/academy/course/course.component.ts new file mode 100644 index 00000000..26477405 --- /dev/null +++ b/src/app/main/content/apps/academy/course/course.component.ts @@ -0,0 +1,101 @@ +import { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { AcademyCourseService } from '../course.service'; +import { Subscription } from 'rxjs/Subscription'; +import { FusePerfectScrollbarDirective } from '../../../../../core/directives/fuse-perfect-scrollbar/fuse-perfect-scrollbar.directive'; +import { fuseAnimations } from '../../../../../core/animations'; + +@Component({ + selector : 'fuse-academy-course', + templateUrl : './course.component.html', + styleUrls : ['./course.component.scss'], + encapsulation: ViewEncapsulation.None, + animations : fuseAnimations +}) +export class FuseAcademyCourseComponent implements OnInit, OnDestroy, AfterViewInit +{ + course: any; + courseSubscription: Subscription; + currentStep = 0; + courseStepContent; + animationDirection: 'left' | 'right' | 'none' = 'none'; + @ViewChildren(FusePerfectScrollbarDirective) fuseScrollbarDirectives: QueryList; + + constructor( + private courseService: AcademyCourseService, + private changeDetectorRef: ChangeDetectorRef + ) + { + + } + + ngOnInit() + { + // Subscribe to courses + this.courseSubscription = + this.courseService.onCourseChanged + .subscribe(course => { + this.course = course; + }); + } + + ngAfterViewInit() + { + this.courseStepContent = this.fuseScrollbarDirectives.find((fuseScrollbarDirective) => { + return fuseScrollbarDirective.element.nativeElement.id === 'course-step-content'; + }); + } + + ngOnDestroy() + { + this.courseSubscription.unsubscribe(); + } + + gotoStep(step) + { + // Decide the animation direction + this.animationDirection = this.currentStep < step ? 'left' : 'right'; + + // Run change detection so the change + // in the animation direction registered + this.changeDetectorRef.detectChanges(); + + // Set the current step + this.currentStep = step; + } + + gotoNextStep() + { + if ( this.currentStep === this.course.totalSteps - 1 ) + { + return; + } + + // Set the animation direction + this.animationDirection = 'left'; + + // Run change detection so the change + // in the animation direction registered + this.changeDetectorRef.detectChanges(); + + // Increase the current step + this.currentStep++; + } + + gotoPreviousStep() + { + if ( this.currentStep === 0 ) + { + return; + } + + // Set the animation direction + this.animationDirection = 'right'; + + // Run change detection so the change + // in the animation direction registered + this.changeDetectorRef.detectChanges(); + + // Decrease the current step + this.currentStep--; + } +} diff --git a/src/app/main/content/apps/academy/courses.service.ts b/src/app/main/content/apps/academy/courses.service.ts new file mode 100644 index 00000000..a1958286 --- /dev/null +++ b/src/app/main/content/apps/academy/courses.service.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; +import { HttpClient } from '@angular/common/http'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Injectable() +export class AcademyCoursesService implements Resolve +{ + onCategoriesChanged: BehaviorSubject = new BehaviorSubject({}); + onCoursesChanged: BehaviorSubject = new BehaviorSubject({}); + + constructor(private http: HttpClient) + { + } + + /** + * The Academy App Main Resolver + * + * @param {ActivatedRouteSnapshot} route + * @param {RouterStateSnapshot} state + * @returns {Observable | Promise | any} + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable | Promise | any + { + return new Promise((resolve, reject) => { + + Promise.all([ + this.getCategories(), + this.getCourses() + ]).then( + () => { + resolve(); + }, + reject + ); + }); + } + + getCategories(): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/academy-categories') + .subscribe((response: any) => { + this.onCategoriesChanged.next(response); + resolve(response); + }, reject); + }); + } + + getCourses(): Promise + { + return new Promise((resolve, reject) => { + this.http.get('api/academy-courses') + .subscribe((response: any) => { + this.onCoursesChanged.next(response); + resolve(response); + }, reject); + }); + } + +} diff --git a/src/app/main/content/apps/academy/courses/courses.component.html b/src/app/main/content/apps/academy/courses/courses.component.html new file mode 100644 index 00000000..d0ff39e0 --- /dev/null +++ b/src/app/main/content/apps/academy/courses/courses.component.html @@ -0,0 +1,97 @@ + + + + + + + school + WELCOME TO ACADEMY + + Our courses will step you through the process of building a small application, or adding a new feature + to an existing application. + + + + + + + + + + + + + + + + + + + + + + + + All + + + {{ category.label }} + + + + + + + + + + + + + + + + + {{course.category}} + + + + access_time + {{course.length}} min + + + + + + {{course.title}} + Updated {{course.updated}} + + + + + + + + + + No courses found! + + + + + + + + + + diff --git a/src/app/main/content/apps/academy/courses/courses.component.scss b/src/app/main/content/apps/academy/courses/courses.component.scss new file mode 100644 index 00000000..30478f74 --- /dev/null +++ b/src/app/main/content/apps/academy/courses/courses.component.scss @@ -0,0 +1,182 @@ +@import "src/app/core/scss/fuse"; + +#academy-courses { + + .header { + position: relative; + flex: 1 0 auto; + height: 280px; + max-height: 280px; + background: #1A237E; + background: linear-gradient(to right, #0E2A3B 0%, #34306B 100%); + text-align: center; + + @include media-breakpoint('xs') { + height: 240px; + padding: 16px; + } + + .hero-text { + + .hero-icon { + position: absolute; + top: -64px; + left: 0px; + opacity: 0.04; + font-size: 512px !important; + width: 512px !important; + height: 512px !important; + } + + h1 { + color: white; + font-size: 40px; + font-weight: 300; + letter-spacing: 0.01em; + text-align: center; + margin-top: 0; + margin-bottom: 16px; + + @include media-breakpoint-down('xs') { + font-size: 24px; + } + } + + h3 { + color: rgba(255, 255, 255, 0.75); + max-width: 480px; + text-align: center; + font-weight: 300; + letter-spacing: 0.03em; + margin: 0; + + @include media-breakpoint-down('xs') { + font-size: 14px; + } + } + } + } + + .content { + + .category-selector { + min-width: 200px; + } + + .filters { + width: 100%; + max-width: 1000px; + margin: 24px auto; + + .course-search { + + @include media-breakpoint-down('xs') { + margin-bottom: 16px; + } + + @include media-breakpoint-up('sm') { + margin-right: 16px; + } + } + } + + .courses { + width: 100%; + max-width: 1040px; + margin: 0 auto; + + .no-courses { + font-size: 24px; + margin: 24px 0; + text-align: center; + } + + .course { + padding: 16px; + + &:hover { + + .course-content { + @include mat-elevation(8); + } + } + + .course-content { + background: white; + min-height: 240px; + transition: box-shadow 150ms ease-in-out; + + @include mat-elevation(1); + + .header { + color: white; + padding: 16px 24px; + height: 64px !important; + min-height: 64px !important; + max-height: 64px !important; + + &.web-bg { + background: mat-color($mat-blue, 500); + } + + &.android-bg { + background: mat-color($mat-green, 500); + } + + &.firebase-bg { + background: mat-color($mat-amber, 800); + } + + &.cloud-bg { + background: mat-color($mat-blue-grey, 500); + } + + .category { + text-transform: capitalize; + text-align: left; + font-weight: 500; + color: rgba(0, 0, 0, 0.54); + } + + .length { + + .length-icon { + margin-right: 8px; + color: rgba(0, 0, 0, 0.54) !important; + } + + .min { + font-size: 16px; + color: rgba(0, 0, 0, 0.54); + } + } + } + + .content { + padding: 24px; + + .h1 { + font-size: 16px; + text-align: center; + } + + .updated { + font-size: 13px; + font-weight: 500; + margin-top: 4px; + color: rgba(0, 0, 0, 0.37); + } + } + + .footer { + padding: 16px; + height: 48px !important; + min-height: 48px !important; + max-height: 48px !important; + box-shadow: inset 0 1px 0 0 rgba(0, 0, 0, 0.12); + } + } + } + } + } +} diff --git a/src/app/main/content/apps/academy/courses/courses.component.ts b/src/app/main/content/apps/academy/courses/courses.component.ts new file mode 100644 index 00000000..bd5572cc --- /dev/null +++ b/src/app/main/content/apps/academy/courses/courses.component.ts @@ -0,0 +1,91 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Subscription } from 'rxjs/Subscription'; +import { AcademyCoursesService } from '../courses.service'; + +@Component({ + selector : 'fuse-academy-courses', + templateUrl: './courses.component.html', + styleUrls : ['./courses.component.scss'] +}) +export class FuseAcademyCoursesComponent implements OnInit, OnDestroy +{ + categories: any[]; + courses: any[]; + coursesFilteredByCategory: any[]; + filteredCourses: any[]; + + categoriesSubscription: Subscription; + coursesSubscription: Subscription; + + currentCategory = 'all'; + searchTerm = ''; + + constructor( + private coursesService: AcademyCoursesService + ) + { + + } + + ngOnInit() + { + // Subscribe to categories + this.categoriesSubscription = + this.coursesService.onCategoriesChanged + .subscribe(categories => { + this.categories = categories; + }); + + // Subscribe to courses + this.coursesSubscription = + this.coursesService.onCoursesChanged + .subscribe(courses => { + this.filteredCourses = this.coursesFilteredByCategory = this.courses = courses; + }); + } + + ngOnDestroy() + { + this.categoriesSubscription.unsubscribe(); + this.coursesSubscription.unsubscribe(); + } + + filterCoursesByCategory() + { + // Filter + if ( this.currentCategory === 'all' ) + { + this.coursesFilteredByCategory = this.courses; + this.filteredCourses = this.courses; + } + else + { + this.coursesFilteredByCategory = this.courses.filter((course) => { + return course.category === this.currentCategory; + }); + + this.filteredCourses = [...this.coursesFilteredByCategory]; + + } + + // Re-filter by search term + this.filterCoursesByTerm(); + } + + filterCoursesByTerm() + { + const searchTerm = this.searchTerm.toLowerCase(); + + // Search + if ( searchTerm === '' ) + { + this.filteredCourses = this.coursesFilteredByCategory; + } + else + { + this.filteredCourses = this.coursesFilteredByCategory.filter((course) => { + return course.title.toLowerCase().includes(searchTerm); + }); + } + } +} diff --git a/src/app/navigation/i18n/en.ts b/src/app/navigation/i18n/en.ts index e6a6da10..497e6c04 100644 --- a/src/app/navigation/i18n/en.ts +++ b/src/app/navigation/i18n/en.ts @@ -6,6 +6,7 @@ export const locale = { 'DASHBOARDS' : 'Dashboards', 'CALENDAR' : 'Calendar', 'ECOMMERCE' : 'E-Commerce', + 'ACADEMY' : 'Academy', 'MAIL' : { 'TITLE': 'Mail', 'BADGE': '25' diff --git a/src/app/navigation/i18n/tr.ts b/src/app/navigation/i18n/tr.ts index 7eb06307..96ca47e4 100644 --- a/src/app/navigation/i18n/tr.ts +++ b/src/app/navigation/i18n/tr.ts @@ -6,6 +6,7 @@ export const locale = { 'DASHBOARDS' : 'Kontrol Paneli', 'CALENDAR' : 'Takvim', 'ECOMMERCE' : 'E-Ticaret', + 'ACADEMY' : 'Akademi', 'MAIL' : { 'TITLE': 'Posta', 'BADGE': '15' diff --git a/src/app/navigation/navigation.model.ts b/src/app/navigation/navigation.model.ts index 074c33b8..a0029669 100644 --- a/src/app/navigation/navigation.model.ts +++ b/src/app/navigation/navigation.model.ts @@ -80,6 +80,14 @@ export class FuseNavigationModel implements FuseNavigationModelInterface } ] }, + { + 'id' : 'academy', + 'title' : 'Academy', + 'translate': 'NAV.ACADEMY', + 'type' : 'item', + 'icon' : 'school', + 'url' : '/apps/academy' + }, { 'id' : 'mail', 'title' : 'Mail',