New: Academy (Courses) app

This commit is contained in:
Sercan Yemen 2017-12-28 10:49:29 +03:00
parent ecae48f3d0
commit a6c56518bc
15 changed files with 1371 additions and 1 deletions

View File

@ -44,6 +44,10 @@ const appRoutes: Routes = [
path : 'apps/e-commerce', path : 'apps/e-commerce',
loadChildren: './main/content/apps/e-commerce/e-commerce.module#FuseEcommerceModule' loadChildren: './main/content/apps/e-commerce/e-commerce.module#FuseEcommerceModule'
}, },
{
path : 'apps/academy',
loadChildren: './main/content/apps/academy/academy.module#FuseAcademyModule'
},
{ {
path : 'apps/todo', path : 'apps/todo',
loadChildren: './main/content/apps/todo/todo.module#FuseTodoModule' loadChildren: './main/content/apps/todo/todo.module#FuseTodoModule'

View File

@ -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 <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> Lorem ipsum dolor sit amet, consectetur adipiscing elit <br><br><br> 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
}
];
}

View File

@ -16,6 +16,7 @@ import { ScrumboardFakeDb } from './scrumboard';
import { FaqFakeDb } from './faq'; import { FaqFakeDb } from './faq';
import { KnowledgeBaseFakeDb } from './knowledge-base'; import { KnowledgeBaseFakeDb } from './knowledge-base';
import { ECommerceFakeDb } from './e-commerce'; import { ECommerceFakeDb } from './e-commerce';
import { AcademyFakeDb } from './academy';
export class FuseFakeDbService implements InMemoryDbService export class FuseFakeDbService implements InMemoryDbService
{ {
@ -52,7 +53,10 @@ export class FuseFakeDbService implements InMemoryDbService
'knowledge-base' : KnowledgeBaseFakeDb.data, 'knowledge-base' : KnowledgeBaseFakeDb.data,
'e-commerce-dashboard' : ECommerceFakeDb.dashboard, 'e-commerce-dashboard' : ECommerceFakeDb.dashboard,
'e-commerce-products' : ECommerceFakeDb.products, '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
}; };
} }
} }

View File

@ -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
{
}

View File

@ -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<any>
{
onCourseChanged: BehaviorSubject<any> = new BehaviorSubject({});
constructor(private http: HttpClient)
{
}
/**
* The Academy App Main Resolver
*
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getCourse(route.params.courseId, route.params.courseSlug)
]).then(
() => {
resolve();
},
reject
);
});
}
getCourse(courseId, courseSlug): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/academy-course/' + courseId + '/' + courseSlug)
.subscribe((response: any) => {
this.onCourseChanged.next(response);
resolve(response);
}, reject);
});
}
}

View File

@ -0,0 +1,100 @@
<div id="academy-course" class="page-layout simple left-sidenav">
<mat-sidenav-container>
<!-- SIDENAV -->
<mat-sidenav class="sidenav" align="start" opened="true" mode="side"
fuseMatSidenavHelper="academy-left-sidenav" mat-is-locked-open="gt-md">
<div class="sidenav-content" fusePerfectScrollbar>
<div class="steps">
<div class="step"
*ngFor="let step of course.steps; let i = index; let last = last; let first = first"
(click)="gotoStep(i)"
[ngClass]="{'current': currentStep === i, 'completed': currentStep > i, 'last': last, 'first': first}">
<div class="index">
<span>{{i + 1}}</span>
</div>
<div class="title">{{step.title}}</div>
</div>
</div>
</div>
</mat-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center">
<!-- HEADER -->
<div class="header mat-accent-bg p-24" fxLayout="row" fxLayoutAlign="start center">
<button mat-button class="mat-icon-button mr-16 sidenav-toggle"
fuseMatSidenavToggler="academy-left-sidenav" fxHide.gt-md>
<mat-icon>menu</mat-icon>
</button>
<button mat-button class="mat-icon-button mr-16" [routerLink]="'/apps/academy/courses'">
<mat-icon>arrow_back</mat-icon>
</button>
<div>
<h2>{{course.title}}</h2>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div id="course-content" class="content">
<ng-container *ngFor="let step of course.steps; let i = index;">
<div class="course-step" fusePerfectScrollbar
*ngIf="currentStep === i"
[@slideIn]="animationDirection">
<div id="course-step-content" class="course-step-content" [innerHTML]="step.content"></div>
</div>
</ng-container>
</div>
<!-- / CONTENT -->
<div class="step-navigation">
<button mat-fab class="previous mat-accent white-fg"
(click)="gotoPreviousStep()"
[disabled]="currentStep === 0"
[fxHide]="currentStep === 0">
<mat-icon>chevron_left</mat-icon>
</button>
<button mat-fab class="next mat-accent white-fg"
(click)="gotoNextStep()"
[disabled]="currentStep === course.totalSteps - 1"
[fxHide]="currentStep === course.totalSteps - 1">
<mat-icon>chevron_right</mat-icon>
</button>
<button mat-fab class="done mat-green-600-bg"
routerLink="/apps/academy/courses"
[disabled]="currentStep !== course.totalSteps - 1"
[fxShow]="currentStep === course.totalSteps - 1">
<mat-icon>check</mat-icon>
</button>
</div>
</div>
<!-- / CENTER -->
</mat-sidenav-container>
</div>

View File

@ -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;
}
}
}
}

View File

@ -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<FusePerfectScrollbarDirective>;
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--;
}
}

View File

@ -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<any>
{
onCategoriesChanged: BehaviorSubject<any> = new BehaviorSubject({});
onCoursesChanged: BehaviorSubject<any> = new BehaviorSubject({});
constructor(private http: HttpClient)
{
}
/**
* The Academy App Main Resolver
*
* @param {ActivatedRouteSnapshot} route
* @param {RouterStateSnapshot} state
* @returns {Observable<any> | Promise<any> | any}
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> | Promise<any> | any
{
return new Promise((resolve, reject) => {
Promise.all([
this.getCategories(),
this.getCourses()
]).then(
() => {
resolve();
},
reject
);
});
}
getCategories(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/academy-categories')
.subscribe((response: any) => {
this.onCategoriesChanged.next(response);
resolve(response);
}, reject);
});
}
getCourses(): Promise<any>
{
return new Promise((resolve, reject) => {
this.http.get('api/academy-courses')
.subscribe((response: any) => {
this.onCoursesChanged.next(response);
resolve(response);
}, reject);
});
}
}

View File

@ -0,0 +1,97 @@
<div id="academy-courses" class="page-layout simple" fusePerfectScrollbar>
<!-- HEADER -->
<div class="header mat-accent-bg p-16 p-sm-24" fxLayout="column" fxLayoutAlign="center center">
<div class="hero-text">
<mat-icon class="hero-icon">school</mat-icon>
<h1>WELCOME TO ACADEMY</h1>
<h3>
Our courses will step you through the process of building a small application, or adding a new feature
to an existing application.
</h3>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content p-24">
<div fxLayout="column" fxLayoutAlign="center">
<div class="filters" fxLayout="row" fxLayoutAlign="center center"
fxLayout-gt.xs="column" fxLayoutAlign.gt-xs="space-between center">
<mat-form-field class="course-search">
<input matInput placeholder="Search for a course" [(ngModel)]="searchTerm"
(input)="filterCoursesByTerm()">
</mat-form-field>
<mat-form-field class="category-selector">
<mat-select placeholder="Select Category" [(ngModel)]="currentCategory"
(change)="filterCoursesByCategory()">
<mat-option [value]="'all'">
All
</mat-option>
<mat-option *ngFor="let category of categories" [value]="category.value">
{{ category.label }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="courses" fxLayout="row" fxLayoutAlign="center" fxLayoutWrap>
<div class="course" *ngFor="let course of filteredCourses" fxFlex="100" fxFlex.gt-xs="50"
fxFlex.gt-sm="33" [ngClass]="course.category">
<div class="course-content" fxLayout="column">
<div class="header" fxLayout="row" fxLayoutAlign="center center"
[ngClass]="course.category + '-bg'">
<div class="category" fxFlex>
{{course.category}}
</div>
<div class="length" fxLayout="row" fxLayoutAlign="center center">
<mat-icon class="length-icon s-20">access_time</mat-icon>
<div class="min">{{course.length}} min</div>
</div>
</div>
<div class="content" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<div class="h1">{{course.title}}</div>
<div class="updated">Updated {{course.updated}}</div>
</div>
<div class="footer" fxLayout="row" fxLayoutAlign="center center">
<button mat-button color="accent"
[routerLink]="'/apps/academy/courses/' + course.id + '/' + course.slug">
START
</button>
</div>
</div>
</div>
<div class="no-courses" *ngIf="filteredCourses.length === 0">
No courses found!
</div>
</div>
</div>
</div>
<!-- / CONTENT -->
</div>

View File

@ -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);
}
}
}
}
}
}

View File

@ -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);
});
}
}
}

View File

@ -6,6 +6,7 @@ export const locale = {
'DASHBOARDS' : 'Dashboards', 'DASHBOARDS' : 'Dashboards',
'CALENDAR' : 'Calendar', 'CALENDAR' : 'Calendar',
'ECOMMERCE' : 'E-Commerce', 'ECOMMERCE' : 'E-Commerce',
'ACADEMY' : 'Academy',
'MAIL' : { 'MAIL' : {
'TITLE': 'Mail', 'TITLE': 'Mail',
'BADGE': '25' 'BADGE': '25'

View File

@ -6,6 +6,7 @@ export const locale = {
'DASHBOARDS' : 'Kontrol Paneli', 'DASHBOARDS' : 'Kontrol Paneli',
'CALENDAR' : 'Takvim', 'CALENDAR' : 'Takvim',
'ECOMMERCE' : 'E-Ticaret', 'ECOMMERCE' : 'E-Ticaret',
'ACADEMY' : 'Akademi',
'MAIL' : { 'MAIL' : {
'TITLE': 'Posta', 'TITLE': 'Posta',
'BADGE': '15' 'BADGE': '15'

View File

@ -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', 'id' : 'mail',
'title' : 'Mail', 'title' : 'Mail',