-
-
{{username}}
+
+
+
+
{{user.username}}
- Update Picture
+ Update Picture
Change Username
+ Change Email
Change Password
- Support
Logout
+
{{ jobDescription }}
+
+
+
diff --git a/src/app/pages/account/account.module.ts b/src/app/pages/account/account.module.ts
index da13bb878..4ac1fba53 100644
--- a/src/app/pages/account/account.module.ts
+++ b/src/app/pages/account/account.module.ts
@@ -4,15 +4,21 @@ import { IonicModule } from '@ionic/angular';
import { AccountPage } from './account';
import { AccountPageRoutingModule } from './account-routing.module';
+import { UploadImageComponent } from '../upload-image/upload-image.component';
+import { DropZoneDirective } from '../../directive/drop-zone.directive';
+import { FileSizePipe } from '../../pipe/file-size.pipe';
@NgModule({
imports: [
CommonModule,
IonicModule,
- AccountPageRoutingModule
+ AccountPageRoutingModule,
],
declarations: [
AccountPage,
+ UploadImageComponent,
+ DropZoneDirective,
+ FileSizePipe
]
})
export class AccountModule { }
diff --git a/src/app/pages/account/account.ts b/src/app/pages/account/account.ts
index d65456d0a..e3b732ffc 100644
--- a/src/app/pages/account/account.ts
+++ b/src/app/pages/account/account.ts
@@ -1,46 +1,84 @@
import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
-
import { AlertController } from '@ionic/angular';
import { UserData } from '../../providers/user-data';
-
+import { User } from '../../models';
+import { FunctionlData } from '../../providers/function-data';
@Component({
selector: 'page-account',
templateUrl: 'account.html',
styleUrls: ['./account.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class AccountPage implements AfterViewInit {
- username: string;
+ header = `User's Info`;
+ users: User[];
+ user: User;
+ succeed: boolean;
+ loadImage: boolean;
+ jobDescription: string;
- constructor(
- public alertCtrl: AlertController,
- public router: Router,
- public userData: UserData
- ) { }
+ constructor(public alertCtrl: AlertController,
+ public router: Router,
+ public userProvider: UserData,
+ private funProvider: FunctionlData) {}
ngAfterViewInit() {
- this.getUsername();
+ this.userProvider.getUsers().subscribe(
+ users => this.users = users
+ );
+ this.userProvider.getUserId().then(id => {
+ this.userProvider.getUserById(id).then(data => { this.user = data; });
+ });
+ }
+
+ updatePicture(path: string) {
+ const oldUrl = this.user.avatar;
+ const id = this.user.id;
+
+ this.user.avatar = path;
+ this.updateUserData(this.user);
+
+ this.loadImage = false;
+ this.user = null;
+ this.userProvider.getUserById(id).then(data => { this.user = data; });
+ this.userProvider.deleteUrl(oldUrl);
}
- updatePicture() {
- console.log('Clicked to update picture');
+ onExit() {
+ this.loadImage = false;
}
// Present an alert with the current username populated
// clicking OK will update the username and display it
// clicking Cancel will close the alert and do nothing
async changeUsername() {
- const alert = await this.alertCtrl.create({
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
header: 'Change Username',
buttons: [
'Cancel',
{
text: 'Ok',
handler: (data: any) => {
- this.userData.setUsername(data.username);
- this.getUsername();
+ if (data.username.trim().length < 4) {
+ this.funProvider.onError(this.header, 'Username should has more then 3 letters. Try again.');
+ } else {
+ if (this.isTheValueUsed(data.username)) {
+ this.funProvider.onError(this.header, data.username + ' was used already. Try another.');
+ } else {
+ // update after getter user's avatar as string.
+ this.userProvider.getUser().then(user => {
+ user.username = data.username;
+ this.updateUserData(user);
+ });
+ this.user.username = data.username;
+ this.succeed = true;
+ this.jobDescription = 'Username has been changed.';
+ }
+ }
}
}
],
@@ -48,26 +86,120 @@ export class AccountPage implements AfterViewInit {
{
type: 'text',
name: 'username',
- value: this.username,
- placeholder: 'username'
+ value: this.user.username,
+ placeholder: 'new username'
+ }
+ ],
+ backdropDismiss: false
+ });
+ await changeForm.present();
+ }
+
+ async changeEmail() {
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Email',
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ if (this.isTheValueUsed(data.email)) {
+ this.funProvider.onError(this.header, data.email + ' was used already. Try another.');
+ } else {
+ // update after getter user's avatar as string.
+ this.userProvider.getUser().then(user => {
+ user.email = data.email;
+ this.updateUserData(user);
+ this.user.email = data.email;
+ this.succeed = true;
+ this.jobDescription = 'Email has been changed.';
+ });
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'email',
+ name: 'email',
+ value: this.user.email,
+ placeholder: 'new email'
}
- ]
+ ],
+ backdropDismiss: false
});
- await alert.present();
+ await changeForm.present();
}
- getUsername() {
- this.userData.getUsername().then((username) => {
- this.username = username;
+ async changePassword() {
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Password',
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ if (this.user.password !== data.currentPW) {
+ this.funProvider.onError(this.header, 'Current password does not match your password.');
+ } else if (data.newPW.length < 4) {
+ this.funProvider.onError(this.header, 'Password should be more than 3 characters.');
+ } else if (data.newPW !== data.confirmPW) {
+ this.funProvider.onError(this.header, 'New password does not match Confirm password.');
+ } else {
+ // update after getter user's avatar as string.
+ this.userProvider.getUser().then(user => {
+ user.password = data.newPW;
+ this.updateUserData(user);
+ this.user.password = data.newPW;
+ this.succeed = true;
+ this.jobDescription = 'Password has been changed.';
+ });
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'password',
+ name: 'newPW',
+ placeholder: 'new password'
+ },
+ {
+ type: 'password',
+ name: 'confirmPW',
+ placeholder: 'confirm password'
+ },
+ {
+ type: 'password',
+ name: 'currentPW',
+ placeholder: 'current password'
+ }
+ ],
+ backdropDismiss: false
});
+ await changeForm.present();
}
- changePassword() {
- console.log('Clicked to change password');
+ isTheValueUsed(value: string) {
+ if (value.indexOf('@') < 0) {
+ return this.users.find(
+ user => user.username.toLowerCase() === value.toLowerCase());
+ }
+ return this.users.find(
+ user => user.email.toLowerCase() === value.toLowerCase());
+ }
+
+ updateUserData(user) {
+ // update logged user info after update user's database
+ this.userProvider.setUser(user).then(() => {
+ this.userProvider.updateUser(user);
+ });
}
logout() {
- this.userData.logout();
+ this.userProvider.logout();
this.router.navigateByUrl('/login');
}
diff --git a/src/app/pages/date-period/date-period.module.ts b/src/app/pages/date-period/date-period.module.ts
new file mode 100644
index 000000000..8ab388201
--- /dev/null
+++ b/src/app/pages/date-period/date-period.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { DatePeriodPage } from './date-period.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: DatePeriodPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [DatePeriodPage]
+})
+export class DatePeriodPageModule {}
diff --git a/src/app/pages/date-period/date-period.page.html b/src/app/pages/date-period/date-period.page.html
new file mode 100644
index 000000000..8f9d2db25
--- /dev/null
+++ b/src/app/pages/date-period/date-period.page.html
@@ -0,0 +1,34 @@
+
+
+
+ Date Period
+
+ Cancel
+ Save Period
+
+
+
+
+
+
+ Enter new Period to make Schedule.
+
+
+
+ From :
+
+
+
+ To :
+
+
+
+
diff --git a/src/app/pages/date-period/date-period.page.scss b/src/app/pages/date-period/date-period.page.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/pages/date-period/date-period.page.ts b/src/app/pages/date-period/date-period.page.ts
new file mode 100644
index 000000000..9c8b905b8
--- /dev/null
+++ b/src/app/pages/date-period/date-period.page.ts
@@ -0,0 +1,57 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { NavParams, ModalController } from '@ionic/angular';
+import { FunctionlData } from '../../providers/function-data';
+
+@Component({
+ selector: 'date-period',
+ templateUrl: './date-period.page.html',
+ styleUrls: ['./date-period.page.scss'],
+})
+export class DatePeriodPage implements OnInit {
+
+ start: string;
+ end: string;
+ minYear: string;
+ maxYear: string;
+
+ constructor(private navParams: NavParams,
+ private modalCtrl: ModalController,
+ private cdRef: ChangeDetectorRef,
+ private funProvider: FunctionlData) { }
+
+ ngOnInit() {
+ this.start = this.navParams.get('start');
+ this.end = this.navParams.get('end');
+ this.minYear = '' + (+this.start.substring(0, 4) - 20);
+ this.maxYear = '' + (+this.end.substring(0, 4) + 20);
+ }
+
+ checkStartDate(value) {
+ this.cdRef.detectChanges();
+ if (this.funProvider.checkDateValidation(value)) {
+ this.end = (this.end < value) ? value : this.end;
+ } else {
+ this.funProvider.onError('Confirm Date', 'The date is not valid. Try again.');
+ this.start = this.navParams.get('start');
+ }
+ }
+
+ checkEndDate(value) {
+ this.cdRef.detectChanges();
+ if (!this.funProvider.checkDateValidation(value)) {
+ this.funProvider.onError('Confirm Date', 'The date is not valid. Try again.');
+ this.end = this.start;
+ } else if (this.start > value) {
+ this.funProvider.onError('Confirm Date', 'The end of period is wrong. Try again.');
+ this.end = this.start;
+ }
+ }
+
+ applySelection() {
+ this.modalCtrl.dismiss({ start: this.start, end: this.end });
+ }
+
+ onExit() {
+ this.modalCtrl.dismiss(null);
+ }
+}
diff --git a/src/app/pages/login/login.html b/src/app/pages/login/login.html
index 4b7d92aa8..b228c32a3 100644
--- a/src/app/pages/login/login.html
+++ b/src/app/pages/login/login.html
@@ -1,5 +1,5 @@
-
+
@@ -17,25 +17,25 @@
Username
-
-
+
Username is required
Password
-
+
-
+
Password is required
diff --git a/src/app/pages/login/login.ts b/src/app/pages/login/login.ts
index eca7d56e2..a4f37f53b 100644
--- a/src/app/pages/login/login.ts
+++ b/src/app/pages/login/login.ts
@@ -1,34 +1,56 @@
-import { Component, ViewEncapsulation } from '@angular/core';
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { UserData } from '../../providers/user-data';
-
-import { UserOptions } from '../../interfaces/user-options';
-
-
+import { User } from '../../models';
+import { FunctionlData } from '../../providers/function-data';
@Component({
selector: 'page-login',
templateUrl: 'login.html',
styleUrls: ['./login.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class LoginPage {
- login: UserOptions = { username: '', password: '' };
+export class LoginPage implements OnInit {
+ username: '';
+ password: '' ;
submitted = false;
+ users: User[];
- constructor(
- public userData: UserData,
- public router: Router
- ) { }
+ constructor(public userData: UserData,
+ public router: Router,
+ private funProvider: FunctionlData,
+ private userProvider: UserData) { }
+
+ ngOnInit() {
+ this.userProvider.getUsers().subscribe(
+ (data: User[]) => { this.users = data; }
+ );
+ }
onLogin(form: NgForm) {
this.submitted = true;
-
if (form.valid) {
- this.userData.login(this.login.username);
- this.router.navigateByUrl('/app/tabs/schedule');
+ const user = this.findUser(this.username.toLowerCase().trim());
+ if (user) {
+ if (user.password === this.password) {
+ this.userData.login(user);
+ this.router.navigateByUrl('/app/tabs/(schedule:schedule)');
+ } else {
+ this.funProvider.onError('Confirm Login', 'Invalid password. Try again.');
+ }
+ } else {
+ this.funProvider.onError('Confirm Login', 'User not found. Try again.');
+ }
+ }
+ }
+
+ findUser(data: string) {
+ if (data.indexOf('@') > -1) {
+ return this.users.find(user => user.email.toLowerCase() === data);
}
+ return this.users.find(user => user.username.toLowerCase() === data);
}
onSignup() {
diff --git a/src/app/pages/map/map.html b/src/app/pages/map/map.html
index 68d390b2f..b234023b6 100644
--- a/src/app/pages/map/map.html
+++ b/src/app/pages/map/map.html
@@ -1,5 +1,5 @@
-
+
diff --git a/src/app/pages/map/map.ts b/src/app/pages/map/map.ts
index 27a531f97..e01a24bc7 100644
--- a/src/app/pages/map/map.ts
+++ b/src/app/pages/map/map.ts
@@ -1,35 +1,41 @@
-import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
+import { Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core';
+
import { ConferenceData } from '../../providers/conference-data';
+
import { Platform } from '@ionic/angular';
+declare var google: any;
+
@Component({
selector: 'page-map',
templateUrl: 'map.html',
- styleUrls: ['./map.scss']
+ styleUrls: ['./map.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class MapPage implements AfterViewInit {
+export class MapPage {
+
@ViewChild('mapCanvas') mapElement: ElementRef;
- constructor(public confData: ConferenceData, public platform: Platform) {}
+ constructor(
+ public dataProvider: ConferenceData,
+ public platform: Platform
+ ) { }
- async ngAfterViewInit() {
- const googleMaps = await getGoogleMaps(
- 'AIzaSyB8pf6ZdFQj5qw7rc_HSGrhUwQKfIe9ICw'
- );
- this.confData.getMap().subscribe((mapData: any) => {
+ ionViewDidEnter() {
+ this.dataProvider.getMap().subscribe((mapData: any) => {
const mapEle = this.mapElement.nativeElement;
- const map = new googleMaps.Map(mapEle, {
- center: mapData.find((d: any) => d.center),
+ const map = new google.maps.Map(mapEle, {
+ center: mapData.find((loc: any) => loc.center),
zoom: 16
});
mapData.forEach((markerData: any) => {
- const infoWindow = new googleMaps.InfoWindow({
+ const infoWindow = new google.maps.InfoWindow({
content: `${markerData.name} `
});
- const marker = new googleMaps.Marker({
+ const marker = new google.maps.Marker({
position: markerData,
map,
title: markerData.name
@@ -40,33 +46,9 @@ export class MapPage implements AfterViewInit {
});
});
- googleMaps.event.addListenerOnce(map, 'idle', () => {
+ google.maps.event.addListenerOnce(map, 'idle', () => {
mapEle.classList.add('show-map');
});
});
}
}
-
-function getGoogleMaps(apiKey: string): Promise {
- const win = window as any;
- const googleModule = win.google;
- if (googleModule && googleModule.maps) {
- return Promise.resolve(googleModule.maps);
- }
-
- return new Promise((resolve, reject) => {
- const script = document.createElement('script');
- script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&v=3.31`;
- script.async = true;
- script.defer = true;
- document.body.appendChild(script);
- script.onload = () => {
- const googleModule2 = win.google;
- if (googleModule2 && googleModule2.maps) {
- resolve(googleModule2.maps);
- } else {
- reject('google maps not available');
- }
- };
- });
-}
diff --git a/src/app/pages/schedule-filter/schedule-filter.html b/src/app/pages/schedule-filter/schedule-filter.html
index ffafd4cd0..a306b6e94 100644
--- a/src/app/pages/schedule-filter/schedule-filter.html
+++ b/src/app/pages/schedule-filter/schedule-filter.html
@@ -1,7 +1,7 @@
-
+
- Cancel
+ Cancel
@@ -19,12 +19,11 @@
Tracks
-
+
{{track.name}}
-
+
-
diff --git a/src/app/pages/schedule-filter/schedule-filter.ts b/src/app/pages/schedule-filter/schedule-filter.ts
index 816631471..4d2966c59 100644
--- a/src/app/pages/schedule-filter/schedule-filter.ts
+++ b/src/app/pages/schedule-filter/schedule-filter.ts
@@ -2,51 +2,54 @@ import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { ConferenceData } from '../../providers/conference-data';
-
+import { UserData } from '../../providers/user-data';
+import { User } from '../../models';
@Component({
selector: 'page-schedule-filter',
templateUrl: 'schedule-filter.html',
styleUrls: ['./schedule-filter.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class ScheduleFilterPage implements AfterViewInit {
-
- tracks: {name: string, isChecked: boolean}[] = [];
+ user: User;
+ trackFilter: { name: string, isChecked: boolean }[] = [];
constructor(
- public confData: ConferenceData,
- public modalCtrl: ModalController
+ public dataProvider: ConferenceData,
+ public modalCtrl: ModalController,
+ public userProvider: UserData
) { }
// TODO use the ionViewDidEnter event
ngAfterViewInit() {
- // passed in array of track names that should be excluded (unchecked)
- const excludedTrackNames = []; // this.navParams.data.excludedTracks;
-
- this.confData.getTracks().subscribe((trackNames: string[]) => {
- trackNames.forEach(trackName => {
- this.tracks.push({
- name: trackName,
- isChecked: (excludedTrackNames.indexOf(trackName) === -1)
- });
- });
+ this.userProvider.getUser().then(user => {
+ this.user = user;
+ this.trackFilter = user.trackFilter;
});
}
resetFilters() {
// reset all of the toggles to be checked
- this.tracks.forEach(track => {
+ this.trackFilter.forEach(track => {
track.isChecked = true;
});
}
applyFilters() {
+ // update user's trackFilter
+ this.user.trackFilter = this.trackFilter;
+ // update loggin user and then update database file.
+ this.userProvider.setUser(this.user).then(() => {
+ this.userProvider.updateUser(this.user);
+ });
+
// Pass back a new array of track names to exclude
- const excludedTrackNames = this.tracks.filter(c => !c.isChecked).map(c => c.name);
+ const excludedTrackNames = this.trackFilter.filter(c => !c.isChecked).map(c => c.name);
this.dismiss(excludedTrackNames);
}
- dismiss(data?: any) {
+ dismiss(data: any) {
// using the injected ModalController this page
// can "dismiss" itself and pass back data
this.modalCtrl.dismiss(data);
diff --git a/src/app/pages/schedule-track/schedule-track.html b/src/app/pages/schedule-track/schedule-track.html
new file mode 100644
index 000000000..d4b1570ed
--- /dev/null
+++ b/src/app/pages/schedule-track/schedule-track.html
@@ -0,0 +1,25 @@
+
+
+
+ One Track Session
+
+
+
+ Cancel
+
+
+
+
+
+
+
+
+ Tracks
+
+
+
+ {{track.name}}
+
+
+
+
diff --git a/src/app/pages/schedule-track/schedule-track.scss b/src/app/pages/schedule-track/schedule-track.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/pages/schedule-track/schedule-track.ts b/src/app/pages/schedule-track/schedule-track.ts
new file mode 100644
index 000000000..f985cb83d
--- /dev/null
+++ b/src/app/pages/schedule-track/schedule-track.ts
@@ -0,0 +1,41 @@
+import { AfterViewInit, Component, ViewEncapsulation } from '@angular/core';
+import { ModalController } from '@ionic/angular';
+
+import { ConferenceData } from '../../providers/conference-data';
+import { UserData } from '../../providers/user-data';
+import { Track } from '../../models';
+
+@Component({
+ selector: 'page-schedule-track',
+ templateUrl: './schedule-track.html',
+ styleUrls: ['./schedule-track.scss'],
+ encapsulation: ViewEncapsulation.None
+
+})
+export class ScheduleTrackPage implements AfterViewInit {
+ tracks: Track[] = [];
+
+ constructor(private dataProvider: ConferenceData,
+ public modalCtrl: ModalController) { }
+
+ ngAfterViewInit() {
+ this.dataProvider.getTracks().subscribe(
+ (tracks: Track[]) => { this.tracks = tracks; }
+ );
+ }
+
+ applyTrack(name: string) {
+ // Pass back a new array of track names to exclude
+ const excludedTracks = [];
+ this.tracks.forEach(
+ (track) => { if (track.name !== name ) { excludedTracks.push(track.name); }
+ });
+ this.dismiss(excludedTracks);
+ }
+
+ dismiss(data: any) {
+ // using the injected ModalController this page
+ // can "dismiss" itself and pass back data
+ this.modalCtrl.dismiss(data);
+ }
+}
diff --git a/src/app/pages/schedule/schedule.html b/src/app/pages/schedule/schedule.html
index 348eafce6..ec9ddc4ce 100644
--- a/src/app/pages/schedule/schedule.html
+++ b/src/app/pages/schedule/schedule.html
@@ -1,13 +1,16 @@
-
+
-
+
All
+
+ Track
+
Favorites
@@ -21,42 +24,49 @@
-
+
-
-
-
-
- {{group.time}}
-
-
-
-
-
+
+
+
+
- {{session.name}}
-
- {{session.timeStart}} — {{session.timeEnd}}: {{session.location}}
-
+ {{daily.date}} : {{group.partOfDay}}
-
-
-
- Favorite
-
-
- Remove
-
-
-
+
+
+
+
+
+ {{session.name}}
+
+ {{session.timeStart}} — {{session.timeEnd}}: {{session.location}}
+
+
+
+
+
+ Favorite
+
+
+ Remove
+
+
+
+
- 0">
- No Sessions Found
-
+
+ Period
+
+ {{ endDate }}
+ {{ startDate }}
+ New
+
+
diff --git a/src/app/pages/schedule/schedule.module.ts b/src/app/pages/schedule/schedule.module.ts
index 77ba69818..c4f745bc9 100644
--- a/src/app/pages/schedule/schedule.module.ts
+++ b/src/app/pages/schedule/schedule.module.ts
@@ -5,7 +5,9 @@ import { IonicModule } from '@ionic/angular';
import { SchedulePage } from './schedule';
import { ScheduleFilterPage } from '../schedule-filter/schedule-filter';
+import { ScheduleTrackPage } from '../schedule-track/schedule-track';
import { SchedulePageRoutingModule } from './schedule-routing.module';
+import { DatePeriodPage } from '../date-period/date-period.page';
@NgModule({
imports: [
@@ -16,10 +18,14 @@ import { SchedulePageRoutingModule } from './schedule-routing.module';
],
declarations: [
SchedulePage,
- ScheduleFilterPage
+ ScheduleFilterPage,
+ ScheduleTrackPage,
+ DatePeriodPage
],
entryComponents: [
- ScheduleFilterPage
+ ScheduleFilterPage,
+ ScheduleTrackPage,
+ DatePeriodPage
]
})
export class ScheduleModule { }
diff --git a/src/app/pages/schedule/schedule.scss b/src/app/pages/schedule/schedule.scss
index 0f366a99d..6cc1ffc80 100644
--- a/src/app/pages/schedule/schedule.scss
+++ b/src/app/pages/schedule/schedule.scss
@@ -17,3 +17,13 @@ $categories: (
padding-left: 10px;
}
}
+
+.notFound {
+ display: inline;
+ text-align: center;
+}
+.notFound h4 {
+ margin-top: 70px;
+ font-size: 24px;
+}
+
diff --git a/src/app/pages/schedule/schedule.ts b/src/app/pages/schedule/schedule.ts
index d603f6e41..4caf0c2d6 100644
--- a/src/app/pages/schedule/schedule.ts
+++ b/src/app/pages/schedule/schedule.ts
@@ -1,53 +1,206 @@
-import { Component, ViewChild, OnInit } from '@angular/core';
+import { Component, ViewChild, OnInit, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
-import { AlertController, IonList, LoadingController, ModalController, ToastController } from '@ionic/angular';
+import { AlertController, List, LoadingController, ModalController, ToastController } from '@ionic/angular';
import { ScheduleFilterPage } from '../schedule-filter/schedule-filter';
+import { ScheduleTrackPage } from '../schedule-track/schedule-track';
import { ConferenceData } from '../../providers/conference-data';
import { UserData } from '../../providers/user-data';
+import { User, Session, PartOfDay } from '../../models';
+import { SessionData } from '../../providers/session-data';
+import { FunctionlData } from '../../providers/function-data';
+import { DatePeriodPage } from '../date-period/date-period.page';
@Component({
selector: 'page-schedule',
templateUrl: 'schedule.html',
styleUrls: ['./schedule.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SchedulePage implements OnInit {
// Gets a reference to the list element
- @ViewChild('scheduleList') scheduleList: IonList;
+ @ViewChild('scheduleList') scheduleList: List;
- dayIndex = 0;
+ user: User;
queryText = '';
- segment = 'all';
- excludeTracks: any = [];
- shownSessions: any = [];
- groups: any = [];
- confDate: string;
+ segment = '';
+ excludeTracks: any[] = [];
+
+ startDate: string;
+ endDate: string;
+ changePeriod = true;
+
+ partsOfDay: PartOfDay[];
+ sessions: Session[];
+ schedule: {
+ date: string,
+ groups: {
+ indexKey: number
+ partOfDay: string,
+ sessions: any[]
+ }[]
+ }[] = [];
constructor(
public alertCtrl: AlertController,
- public confData: ConferenceData,
+ public dataProvider: ConferenceData,
+ private sessionProvider: SessionData,
public loadingCtrl: LoadingController,
public modalCtrl: ModalController,
public router: Router,
public toastCtrl: ToastController,
- public user: UserData
+ public userProvider: UserData,
+ private funProvider: FunctionlData
) { }
ngOnInit() {
- // this.app.setTitle('Schedule');
- this.updateSchedule();
+ this.userProvider.isLoggedIn().then(loggedIn => {
+ if (loggedIn) {
+ this.dataProvider.getPeriod().then(period => {
+ if (period) {
+ this.startDate = period.start;
+ this.endDate = period.end;
+ this.updateSchedule();
+ } else {
+ this.getDatePeriod();
+ }
+ });
+ }
+ });
+ }
+
+ async getDatePeriod() {
+ const start = this.startDate ? this.startDate : this.funProvider.getDateFormat();
+ const end = this.endDate ? this.endDate : start ;
+ const modal = await this.modalCtrl.create({
+ component: DatePeriodPage,
+ componentProps: { start, end }
+ });
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.startDate = data.start;
+ this.endDate = data.end;
+ this.schedule = [];
+ this.changePeriod = true;
+ this.dataProvider.setPeriod({ start: this.startDate, end: this.endDate });
+ this.updateSchedule();
+ }
}
updateSchedule() {
- // Close any open sliding items when the schedule updates
- if (this.scheduleList) {
- this.scheduleList.closeSlidingItems();
+ this.userProvider.getUser().then(user => {
+ user.trackFilter.forEach(track => {
+ if (!track.isChecked) { this.excludeTracks.push(track.name); }
+ });
+ this.user = user;
+ this.dataProvider.getPartsOfDay().subscribe(
+ response => { this.partsOfDay = response ; }
+ );
+
+ // Close any open sliding items when the schedule updates
+ if (this.scheduleList) {
+ this.scheduleList.closeSlidingItems();
+ }
+
+ if (this.schedule.length === 0 || this.changePeriod) {
+ this.changePeriod = false;
+ this.sessionProvider.getSessionsInPeriod(this.startDate, this.endDate).subscribe(
+ (response: Session[]) => {
+ this.sessions = response;
+ this.buildSchedule();
+ });
+ }
+ });
+ }
+
+ getFilterOption() {
+ return {
+ queryText: this.queryText.toLowerCase().trim(),
+ segment: this.segment,
+ excludeTracks: this.excludeTracks
+ };
+ }
+
+ buildSchedule() {
+ const filterOption = this.getFilterOption();
+ this.sessions.forEach(session => {
+ session = this.dataProvider.filterSession(session, filterOption);
+ const partOfDay = this.partsOfDay.find(part => part.timeFrom <= session.timeStart && part.timeTo >= session.timeStart);
+ const newGroup = {
+ indexKey: partOfDay.indexKey,
+ partOfDay: partOfDay.name,
+ sessions: [session]
+ };
+ const newItem = { date: session.date, groups: [newGroup]};
+
+ const sIndex = this.schedule.findIndex(item => item.date === session.date);
+ if (sIndex < 0) {
+ this.schedule.push(newItem);
+ } else {
+ const gIndex = this.schedule[sIndex].groups.findIndex(
+ group => group.partOfDay === partOfDay.name
+ );
+ if (gIndex < 0) {
+ this.schedule[sIndex].groups.push(newGroup);
+ } else {
+ this.schedule[sIndex].groups[gIndex].sessions.push(session);
+ }
+ }
+ });
+ this.schedule.sort((a, b) => {
+ if (a.date > b.date) { return 1; }
+ return -1;
+ });
+ this.schedule.forEach(item => {
+ item.groups.sort((a, b) => a.indexKey - b.indexKey);
+ item.groups.forEach(group => {
+ group.sessions.sort((a, b) => {
+ if (a.timeStart > b.timeStart) { return 1; }
+ return -1;
+ });
+ });
+ });
+ }
+
+ updateFilter() {
+ const filterOption = this.getFilterOption();
+ this.schedule.forEach(daily => {
+ daily.groups.forEach(group => {
+ group.sessions.forEach(session => {
+ session = this.dataProvider.filterSession(session, filterOption);
+ });
+ });
+ });
+ }
+
+ processBySegment() {
+ if (this.segment === 'one') {
+ this.chooseTrack();
+ } else if (this.segment === 'all') {
+ this.excludeTracks = [];
+ this.updateFilter();
+ } else if (this.segment === 'favorites') {
+ this.updateFilter();
}
+ }
- this.confData.getTimeline(this.dayIndex, this.queryText, this.excludeTracks, this.segment).subscribe((data: any) => {
- this.shownSessions = data.shownSessions;
- this.groups = data.groups;
+ async chooseTrack() {
+ const modal = await this.modalCtrl.create({
+ component: ScheduleTrackPage,
+ componentProps: { excludedTracks: this.excludeTracks }
});
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.excludeTracks = data;
+ this.segment = 'user';
+ this.updateFilter();
+ } else {
+ this.segment = '';
+ }
}
async presentFilter() {
@@ -60,18 +213,25 @@ export class SchedulePage implements OnInit {
const { data } = await modal.onWillDismiss();
if (data) {
this.excludeTracks = data;
- this.updateSchedule();
+ this.segment = 'user';
+ this.updateFilter();
}
}
- async addFavorite(slidingItem: HTMLIonItemSlidingElement, sessionData: any) {
- if (this.user.hasFavorite(sessionData.name)) {
+ goToSessionDetail(sessionData: any) {
+ // go to the session detail page
+ // and pass in the session data
+ this.router.navigateByUrl(`app/tabs/(schedule:session/${sessionData.id})`);
+ }
+
+async addFavorite(slidingItem: HTMLIonItemSlidingElement, sessionData: any) {
+ if (this.userProvider.hasFavorite(sessionData.name)) {
// woops, they already favorited it! What shall we do!?
// prompt them to remove it
this.removeFavorite(slidingItem, sessionData, 'Favorite already added');
} else {
// remember this session as a user favorite
- this.user.addFavorite(sessionData.name);
+ this.userProvider.addFavorite(sessionData.name);
// create an alert instance
const alert = await this.alertCtrl.create({
@@ -107,7 +267,7 @@ export class SchedulePage implements OnInit {
text: 'Remove',
handler: () => {
// they want to remove this session from their favorites
- this.user.removeFavorite(sessionData.name);
+ this.userProvider.removeFavorite(sessionData.name);
this.updateSchedule();
// close the sliding item and hide the option buttons
diff --git a/src/app/pages/session-detail/session-detail.html b/src/app/pages/session-detail/session-detail.html
index 34e3dafb6..43da81ff5 100644
--- a/src/app/pages/session-detail/session-detail.html
+++ b/src/app/pages/session-detail/session-detail.html
@@ -1,7 +1,7 @@
-
+
-
+
{{session?.name}}
@@ -12,11 +12,14 @@
- {{track}}
+ {{track}}
-
-
+
+
diff --git a/src/app/pages/session-detail/session-detail.ts b/src/app/pages/session-detail/session-detail.ts
index 80b0c7daa..898f2bdee 100644
--- a/src/app/pages/session-detail/session-detail.ts
+++ b/src/app/pages/session-detail/session-detail.ts
@@ -1,8 +1,9 @@
import { Component } from '@angular/core';
-
-import { ConferenceData } from '../../providers/conference-data';
import { ActivatedRoute } from '@angular/router';
+
import { UserData } from '../../providers/user-data';
+import { SessionData } from '../../providers/session-data';
+import { Session, User } from '../../models';
@Component({
selector: 'page-session-detail',
@@ -10,52 +11,46 @@ import { UserData } from '../../providers/user-data';
templateUrl: 'session-detail.html'
})
export class SessionDetailPage {
- session: any;
+ session: Session;
+ user: User;
isFavorite = false;
- defaultHref = '';
+
constructor(
- private dataProvider: ConferenceData,
+ private sessionProvider: SessionData,
private userProvider: UserData,
private route: ActivatedRoute
) {}
+
+ ionViewWillEnter() {
+ const id = this.route.snapshot.paramMap.get('sessionId');
+ this.sessionProvider.getSession(id)
+ .then( data => {
+ data.id = id;
+ this.session = data;
+ this.userProvider.getUser().then(user => {
+ this.user = user;
+ this.user.favorites.forEach(favorite => {
+ if (favorite.id === this.session.id) { this.isFavorite = true; }
+ });
+ });
+ }
+ );
+ }
+
sessionClick(item: string) {
console.log('Clicked', item);
}
+
toggleFavorite() {
- if (this.userProvider.hasFavorite(this.session.name)) {
- this.userProvider.removeFavorite(this.session.name);
- this.isFavorite = false;
+ if (this.isFavorite) {
+ const index = this.user.favorites.findIndex(f => f.id === this.session.id);
+ if (index > -1) {
+ this.user.favorites.splice(index, 1);
+ }
} else {
- this.userProvider.addFavorite(this.session.name);
- this.isFavorite = true;
+ this.user.favorites.push({id: this.session.id, name: this.session.name });
}
- }
- ionViewWillEnter() {
- this.dataProvider.load().subscribe((data: any) => {
- if (
- data &&
- data.schedule &&
- data.schedule[0] &&
- data.schedule[0].groups
- ) {
- const sessionId = this.route.snapshot.paramMap.get('sessionId');
- for (const group of data.schedule[0].groups) {
- if (group && group.sessions) {
- for (const session of group.sessions) {
- if (session && session.id === sessionId) {
- this.session = session;
- this.isFavorite = this.userProvider.hasFavorite(
- this.session.name
- );
- break;
- }
- }
- }
- }
- }
- });
- }
- ionViewDidEnter() {
- this.defaultHref = `/app/tabs/schedule`;
+ this.userProvider.updateUser(this.user);
+ this.isFavorite = !this.isFavorite;
}
}
diff --git a/src/app/pages/signup/signup.html b/src/app/pages/signup/signup.html
index 38c5bed9c..fd0f2fdeb 100644
--- a/src/app/pages/signup/signup.html
+++ b/src/app/pages/signup/signup.html
@@ -1,5 +1,5 @@
-
+
@@ -26,9 +26,21 @@
+
+ Email
+
+
+
+
+
+ Email is required
+
+
+
Password
-
+
@@ -36,6 +48,22 @@
Password is required
+
+
+ Confirm Password
+
+
+
+
+
+ Confirm Password is required
+
+
+
+
+ Password does not match with Confirm Password
+
+
diff --git a/src/app/pages/signup/signup.ts b/src/app/pages/signup/signup.ts
index 1bc639081..17cc556d9 100644
--- a/src/app/pages/signup/signup.ts
+++ b/src/app/pages/signup/signup.ts
@@ -1,33 +1,70 @@
-import { Component, ViewEncapsulation } from '@angular/core';
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Router } from '@angular/router';
import { UserData } from '../../providers/user-data';
-
-import { UserOptions } from '../../interfaces/user-options';
-
-
+import { ConferenceData } from '../../providers/conference-data';
+import { User, Track } from '../../models';
+import { FunctionlData } from '../../providers/function-data';
@Component({
selector: 'page-signup',
templateUrl: 'signup.html',
styleUrls: ['./signup.scss'],
+ encapsulation: ViewEncapsulation.None
})
-export class SignupPage {
- signup: UserOptions = { username: '', password: '' };
+export class SignupPage implements OnInit {
+ header = 'Confirm Signup';
+ users: User[];
+ signup: User = {
+ username: '', password: '', email: '', favorites: [], trackFilter: []
+ };
+ tracks: Track[];
+ confirmPassword = '';
submitted = false;
- constructor(
- public router: Router,
- public userData: UserData
- ) {}
+ constructor(public router: Router,
+ public userProvider: UserData,
+ private funProvider: FunctionlData,
+ public dataProvider: ConferenceData) {}
+
+ ngOnInit() {
+ this.userProvider.getUsers().subscribe(
+ (data: User[]) => { this.users = data; }
+ );
+ this.dataProvider.getTracks().subscribe(
+ (tracks: Track[]) => { this.tracks = tracks; }
+ );
+ }
onSignup(form: NgForm) {
this.submitted = true;
-
- if (form.valid) {
- this.userData.signup(this.signup.username);
- this.router.navigateByUrl('/app/tabs/schedule');
+ if (form.valid && this.signup.password === this.confirmPassword) {
+ if (this.signup.username.trim().length < 4) {
+ this.funProvider.onError(this.header, 'Name should has more then 3 letters. Try again.');
+ } else if (this.isNameUsed(this.signup.username)) {
+ this.funProvider.onError(this.header, 'Name is already taken. Try another.');
+ } else if (this.isEmailUsed(this.signup.email)) {
+ this.funProvider.onError(this.header, 'Email is already taken. Try another.');
+ } else {
+ this.setTrackFilter();
+ this.userProvider.signup(this.signup);
+ this.router.navigateByUrl('/app/tabs/(speakers:speakers)');
+ }
}
}
+
+ setTrackFilter() {
+ this.tracks.forEach(track => {
+ this.signup.trackFilter.push({ name: track.name, isChecked: true });
+ });
+ }
+
+ isNameUsed(name) {
+ return this.users.find(ur => ur.username.toLowerCase() === name.toLowerCase());
+ }
+
+ isEmailUsed(email) {
+ return this.users.find(ur => ur.email.toLowerCase() === email.toLowerCase());
+ }
}
diff --git a/src/app/pages/speaker-detail/speaker-detail.html b/src/app/pages/speaker-detail/speaker-detail.html
index d50199ab3..1eb361bd7 100644
--- a/src/app/pages/speaker-detail/speaker-detail.html
+++ b/src/app/pages/speaker-detail/speaker-detail.html
@@ -1,7 +1,7 @@
-
+
-
+
{{speaker?.name}}
@@ -12,16 +12,20 @@
-
+
-
+
-
+
{{speaker?.about}}
+ {{speaker?.location}}
+ {{speaker?.email}}
+ {{speaker?.phone}}
+ Sessions involved: {{speaker?.sessions.length}}
diff --git a/src/app/pages/speaker-detail/speaker-detail.ts b/src/app/pages/speaker-detail/speaker-detail.ts
index fb148a176..29729b5d5 100644
--- a/src/app/pages/speaker-detail/speaker-detail.ts
+++ b/src/app/pages/speaker-detail/speaker-detail.ts
@@ -1,32 +1,55 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { ConferenceData } from '../../providers/conference-data';
+import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
+
+import { SpeakerData } from '../../providers/speaker-data';
+import { Speaker } from '../../models';
@Component({
selector: 'page-speaker-detail',
templateUrl: 'speaker-detail.html',
styleUrls: ['./speaker-detail.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SpeakerDetailPage {
- speaker: any;
+ speaker: Speaker;
constructor(
- private dataProvider: ConferenceData,
+ private speakerProvider: SpeakerData,
private router: Router,
- private route: ActivatedRoute
+ private route: ActivatedRoute,
+ public inAppBrowser: InAppBrowser
) {}
ionViewWillEnter() {
- this.dataProvider.load().subscribe((data: any) => {
- const speakerId = this.route.snapshot.paramMap.get('speakerId');
- if (data && data.speakers) {
- for (const speaker of data.speakers) {
- if (speaker && speaker.id === speakerId) {
- this.speaker = speaker;
- break;
- }
- }
- }
- });
+ const id = this.route.snapshot.paramMap.get('speakerId');
+ this.speakerProvider.getSpeaker(id)
+ .then( data => { this.speaker = data; }
+ );
+ }
+
+ goToSessionDetail(session: any) {
+ this.router.navigateByUrl(`app/tabs/(schedule:session/${session.id})`);
+ }
+
+ goToSpeakerTwitter() {
+ this.inAppBrowser.create(
+ `https://twitter.com/${this.speaker.twitter}`,
+ '_blank'
+ );
+ }
+
+ goToSpeakergithub() {
+ this.inAppBrowser.create(
+ `https://github.com/${this.speaker.github}`,
+ '_blank'
+ );
+ }
+
+ goToSpeakerInstagram() {
+ this.inAppBrowser.create(
+ `https://instagram.com/${this.speaker.instagram}`,
+ '_blank'
+ );
}
}
diff --git a/src/app/pages/speaker-list/speaker-list.html b/src/app/pages/speaker-list/speaker-list.html
index 05cd974a4..ee839b0c7 100644
--- a/src/app/pages/speaker-list/speaker-list.html
+++ b/src/app/pages/speaker-list/speaker-list.html
@@ -1,10 +1,13 @@
-
+
Speakers
+
+
+
@@ -12,9 +15,10 @@
-
+
-
+
@@ -24,11 +28,11 @@
-
+
{{session.name}}
-
+
About {{speaker.name}}
diff --git a/src/app/pages/speaker-list/speaker-list.module.ts b/src/app/pages/speaker-list/speaker-list.module.ts
index c99e4bcbf..b4c75b330 100644
--- a/src/app/pages/speaker-list/speaker-list.module.ts
+++ b/src/app/pages/speaker-list/speaker-list.module.ts
@@ -4,11 +4,13 @@ import { IonicModule } from '@ionic/angular';
import { SpeakerListPage } from './speaker-list';
import { SpeakerListPageRoutingModule } from './speaker-list-routing.module';
+import { FormsModule } from '@angular/forms';
@NgModule({
imports: [
CommonModule,
IonicModule,
+ FormsModule,
SpeakerListPageRoutingModule
],
declarations: [SpeakerListPage],
diff --git a/src/app/pages/speaker-list/speaker-list.ts b/src/app/pages/speaker-list/speaker-list.ts
index e50e5debd..de568d846 100644
--- a/src/app/pages/speaker-list/speaker-list.ts
+++ b/src/app/pages/speaker-list/speaker-list.ts
@@ -1,32 +1,65 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Router } from '@angular/router';
import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
-import { ActionSheetController } from '@ionic/angular';
+import { ActionSheetController, AlertController } from '@ionic/angular';
-import { ConferenceData } from '../../providers/conference-data';
+import { Speaker } from '../../models';
+import { SpeakerData } from '../../providers/speaker-data';
+import { UserData } from '../../providers/user-data';
@Component({
selector: 'page-speaker-list',
templateUrl: 'speaker-list.html',
styleUrls: ['./speaker-list.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SpeakerListPage {
- speakers: any[] = [];
+
+ queryText = '';
+ speakers: Speaker[];
constructor(
public actionSheetCtrl: ActionSheetController,
- public confData: ConferenceData,
+ public alertCtrl: AlertController,
+ private speakerProvider: SpeakerData,
public inAppBrowser: InAppBrowser,
+ private userProvider: UserData,
public router: Router
) {}
ionViewDidEnter() {
- this.confData.getSpeakers().subscribe((speakers: any[]) => {
- this.speakers = speakers;
- });
+ this.speakerProvider.getSpeakers().subscribe(
+ speakers => { this.speakers = speakers; }
+ );
+ // this.userProvider.isLoggedIn().then(loggedIn => {
+ // if (loggedIn) { this.askLogIn(); }
+ // })
}
- goToSpeakerTwitter(speaker: any) {
+ // async askLogIn() {
+ // const askLogInForm = await this.alertCtrl.create({
+ // header: 'Recommendation',
+ // subHeader: 'You need to login to access all features.',
+ // buttons: [
+ // {
+ // text: 'Login Now',
+ // handler: () => {
+ // this.router.navigate(['/login']);
+ // }
+ // },
+ // {
+ // text: 'Login later',
+ // handler: () => {
+
+ // }
+ // },
+ // ],
+ // backdropDismiss: false
+ // });
+ // await askLogInForm.present();
+ // }
+
+ goToSpeakerTwitter(speaker: Speaker) {
this.inAppBrowser.create(
`https://twitter.com/${speaker.twitter}`,
'_blank'
diff --git a/src/app/pages/support/support.html b/src/app/pages/support/support.html
index 278e9856f..44e69e5e2 100644
--- a/src/app/pages/support/support.html
+++ b/src/app/pages/support/support.html
@@ -1,14 +1,13 @@
-
+
Support
-
-
+
@@ -26,9 +25,12 @@
Support message is required
-
Submit
+
+ You need to login first!
+
+
diff --git a/src/app/pages/support/support.scss b/src/app/pages/support/support.scss
index e6e125144..ca558fcfd 100644
--- a/src/app/pages/support/support.scss
+++ b/src/app/pages/support/support.scss
@@ -1,13 +1,19 @@
-.support-logo {
- padding: 20px 0;
- min-height: 200px;
- text-align: center;
-}
+page-support {
+ .support-logo {
+ padding: 20px 0;
+ min-height: 200px;
+ text-align: center;
+ }
-.support-logo img {
- max-width: 150px;
-}
+ .support-logo img {
+ max-width: 150px;
+ }
-.list {
- margin-bottom: 0;
-}
+ .list {
+ margin-bottom: 0;
+ }
+
+ .support-msg h4 {
+ margin: 200px 80px;
+ }
+}
\ No newline at end of file
diff --git a/src/app/pages/support/support.ts b/src/app/pages/support/support.ts
index 4d55f8e5d..d32db0846 100644
--- a/src/app/pages/support/support.ts
+++ b/src/app/pages/support/support.ts
@@ -1,38 +1,41 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { NgForm } from '@angular/forms';
+import { Storage } from '@ionic/storage';
import { AlertController, ToastController } from '@ionic/angular';
-
+import { UserData } from '../../providers/user-data';
+import { SupportData } from '../../providers/support-data';
@Component({
selector: 'page-support',
templateUrl: 'support.html',
styleUrls: ['./support.scss'],
+ encapsulation: ViewEncapsulation.None
})
export class SupportPage {
+ isLoggedIn = false;
submitted = false;
supportMessage: string;
- constructor(
- public alertCtrl: AlertController,
- public toastCtrl: ToastController
- ) { }
+ constructor(public alertCtrl: AlertController,
+ public toastCtrl: ToastController,
+ private userData: UserData,
+ private supportData: SupportData,
+ public storage: Storage) {
+ }
async ionViewDidEnter() {
- const toast = await this.toastCtrl.create({
- message: 'This does not actually send a support request.',
- duration: 3000
- });
- await toast.present();
+ this.userData.isLoggedIn()
+ .then(res => this.isLoggedIn = res);
}
async submit(form: NgForm) {
this.submitted = true;
if (form.valid) {
+ this.supportData.addSupport(this.supportMessage);
this.supportMessage = '';
this.submitted = false;
-
const toast = await this.toastCtrl.create({
message: 'Your support request has been sent.',
duration: 3000
@@ -40,26 +43,4 @@ export class SupportPage {
await toast.present();
}
}
-
- // If the user enters text in the support question and then navigates
- // without submitting first, ask if they meant to leave the page
- // async ionViewCanLeave(): Promise {
- // // If the support message is empty we should just navigate
- // if (!this.supportMessage || this.supportMessage.trim().length === 0) {
- // return true;
- // }
-
- // return new Promise((resolve: any, reject: any) => {
- // const alert = await this.alertCtrl.create({
- // title: 'Leave this page?',
- // message: 'Are you sure you want to leave this page? Your support message will not be submitted.',
- // buttons: [
- // { text: 'Stay', handler: reject },
- // { text: 'Leave', role: 'cancel', handler: resolve }
- // ]
- // });
-
- // await alert.present();
- // });
- // }
}
diff --git a/src/app/pages/tabs-page/tabs-page-routing.module.ts b/src/app/pages/tabs-page/tabs-page-routing.module.ts
index fa31409c5..6ebfeac42 100644
--- a/src/app/pages/tabs-page/tabs-page-routing.module.ts
+++ b/src/app/pages/tabs-page/tabs-page-routing.module.ts
@@ -1,7 +1,14 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
+
import { TabsPage } from './tabs-page';
+
+import { AboutPage } from '../about/about';
+import { MapPage } from '../map/map';
import { SchedulePage } from '../schedule/schedule';
+import { SessionDetailPage } from '../session-detail/session-detail';
+import { SpeakerDetailPage } from '../speaker-detail/speaker-detail';
+import { SpeakerListPage } from '../speaker-list/speaker-list';
const routes: Routes = [
@@ -9,58 +16,44 @@ const routes: Routes = [
path: 'tabs',
component: TabsPage,
children: [
+ // tab one
{
path: 'schedule',
- children: [
- {
- path: '',
- component: SchedulePage,
- },
- {
- path: 'session/:sessionId',
- loadChildren: '../session-detail/session-detail.module#SessionDetailModule'
- }
- ]
+ component: SchedulePage,
+ outlet: 'schedule'
+ },
+ {
+ path: 'session/:sessionId',
+ component: SessionDetailPage,
+ outlet: 'schedule'
},
+ // tab two
{
path: 'speakers',
- children: [
- {
- path: '',
- loadChildren: '../speaker-list/speaker-list.module#SpeakerListModule'
- },
- {
- path: 'session/:sessionId',
- loadChildren: '../session-detail/session-detail.module#SessionDetailModule'
- },
- {
- path: 'speaker-details/:speakerId',
- loadChildren: '../speaker-detail/speaker-detail.module#SpeakerDetailModule'
- }
- ]
+ component: SpeakerListPage,
+ outlet: 'speakers'
},
{
- path: 'map',
- children: [
- {
- path: '',
- loadChildren: '../map/map.module#MapModule'
- }
- ]
+ path: 'session/:sessionId',
+ component: SessionDetailPage,
+ outlet: 'speakers'
},
{
- path: 'about',
- children: [
- {
- path: '',
- loadChildren: '../about/about.module#AboutModule'
- }
- ]
+ path: 'speaker-details/:speakerId',
+ component: SpeakerDetailPage,
+ outlet: 'speakers'
},
+ // tab three
{
- path: '',
- redirectTo: '/app/tabs/schedule',
- pathMatch: 'full'
+ path: 'map',
+ component: MapPage,
+ outlet: 'map'
+ },
+ // tab four
+ {
+ path: 'about',
+ component: AboutPage,
+ outlet: 'about'
}
]
}
@@ -71,4 +64,3 @@ const routes: Routes = [
exports: [RouterModule]
})
export class TabsPageRoutingModule { }
-
diff --git a/src/app/pages/tabs-page/tabs-page.html b/src/app/pages/tabs-page/tabs-page.html
index d7da8656e..efd19590a 100644
--- a/src/app/pages/tabs-page/tabs-page.html
+++ b/src/app/pages/tabs-page/tabs-page.html
@@ -1,25 +1,42 @@
-
+
Schedule
-
+
Speakers
-
+
Map
-
+
About
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/pages/tabs-page/tabs-page.ts b/src/app/pages/tabs-page/tabs-page.ts
index 0afde6465..c096955cc 100644
--- a/src/app/pages/tabs-page/tabs-page.ts
+++ b/src/app/pages/tabs-page/tabs-page.ts
@@ -1,6 +1,17 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
+import { UserData } from '../../providers/user-data';
@Component({
templateUrl: 'tabs-page.html'
})
-export class TabsPage {}
+export class TabsPage implements OnInit {
+ loggedIn = false;
+
+ constructor(private userProvide: UserData) {}
+
+ ngOnInit() {
+ this.userProvide.isLoggedIn().then(data => {
+ this.loggedIn = data;
+ });
+ }
+}
diff --git a/src/app/pages/tutorial/tutorial.html b/src/app/pages/tutorial/tutorial.html
index d30cc9591..a1beccc8d 100644
--- a/src/app/pages/tutorial/tutorial.html
+++ b/src/app/pages/tutorial/tutorial.html
@@ -19,6 +19,9 @@
ionic conference app is a practical preview of the ionic framework in action, and a demonstration of proper code
use.
+
+
+
@@ -27,6 +30,10 @@ What is Ionic?
Ionic Framework is an open source SDK that enables developers to build high quality mobile apps with web technologies
like HTML, CSS, and JavaScript.
+
+
+
+
@@ -35,6 +42,10 @@ What is Ionic Pro?
Ionic Pro is a powerful set of services and features built on top of Ionic Framework that brings a totally new
level of app development agility to mobile dev teams.
+
+
+
+
@@ -42,8 +53,11 @@ What is Ionic Pro?
Ready to Play?
Continue
-
+
+
+
+
diff --git a/src/app/pages/tutorial/tutorial.scss b/src/app/pages/tutorial/tutorial.scss
index 4a8019f87..cdf028cd1 100644
--- a/src/app/pages/tutorial/tutorial.scss
+++ b/src/app/pages/tutorial/tutorial.scss
@@ -18,6 +18,32 @@ ion-toolbar {
margin: 36px 0;
}
+.btn-right {
+ float: right;
+ margin-top: 20px;
+}
+
+.btn-left {
+ margin-top: 50px;
+}
+
+.btn-both {
+ display: inline;
+}
+
+.arrow-forward {
+ float: right;
+ width: 30px;
+ margin-right: 20px;
+ font-size: 30px;
+}
+
+.arrow-back {
+ float: left;
+ margin-left: 20px;
+ font-size: 30px;
+}
+
b {
font-weight: 500;
}
diff --git a/src/app/pages/tutorial/tutorial.ts b/src/app/pages/tutorial/tutorial.ts
index 79fc63ca3..b4e97b353 100644
--- a/src/app/pages/tutorial/tutorial.ts
+++ b/src/app/pages/tutorial/tutorial.ts
@@ -1,9 +1,10 @@
-import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
+import { Component, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
-import { MenuController, IonSlides } from '@ionic/angular';
+import { MenuController, Slides } from '@ionic/angular';
import { Storage } from '@ionic/storage';
+import { UserData } from '../../providers/user-data';
@Component({
selector: 'page-tutorial',
@@ -13,17 +14,18 @@ import { Storage } from '@ionic/storage';
export class TutorialPage {
showSkip = true;
- @ViewChild('slides') slides: IonSlides;
+ @ViewChild('slides') slides: Slides;
constructor(
public menu: MenuController,
+ private userProvider: UserData,
public router: Router,
public storage: Storage
) {}
startApp() {
this.router
- .navigateByUrl('/app/tabs/schedule')
+ .navigateByUrl('/app/tabs/(speakers:speakers)')
.then(() => this.storage.set('ion_did_tutorial', 'true'));
}
@@ -34,6 +36,11 @@ export class TutorialPage {
}
ionViewWillEnter() {
+ this.userProvider.isLoggedIn().then(res => {
+ if (res) {
+ this.router.navigateByUrl('/app/tabs/(schedule:schedule)');
+ }
+ });
this.storage.get('ion_did_tutorial').then(res => {
if (res === true) {
this.router.navigateByUrl('/app/tabs/schedule');
diff --git a/src/app/pages/upload-image/upload-image.component.html b/src/app/pages/upload-image/upload-image.component.html
new file mode 100644
index 000000000..38bbdb835
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.html
@@ -0,0 +1,26 @@
+Upload User's Avatar
+
+
+
Image Drop Zone
+
Drag and Drop a File
+
+
+
+
+
+Cancel
+
+
+
+ {{ pct | number }}%
+
+
+
+ {{ snap.bytesTransferred | fileSize }} of {{ snap.totalBytes | fileSize }}
+
diff --git a/src/app/pages/upload-image/upload-image.component.scss b/src/app/pages/upload-image/upload-image.component.scss
new file mode 100644
index 000000000..3c4bf3bd1
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.scss
@@ -0,0 +1,43 @@
+.title {
+ font-size: 1.4em;
+ margin-top: 40px;
+}
+
+.dropzone {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ font-weight: 200;
+ height: 300px;
+ border: 2px dashed #f16624;
+ border-radius: 5px;
+ background: white;
+ margin: 40px 0;
+
+ &.hovering {
+ border: 2px solid #f16624;
+ color: #dadada !important;
+ }
+
+ .file-label {
+ font-size: 1em;
+ }
+}
+
+progress::-webkit-progress-value {
+ transition: width 0.1s ease;
+}
+
+img {
+ width: 250px
+}
+
+button {
+ padding: 10px;
+ width: 150px;
+ font-size: 1em;
+ font-weight: 400;
+ background-color: #f16624;
+ color: white
+}
diff --git a/src/app/pages/upload-image/upload-image.component.ts b/src/app/pages/upload-image/upload-image.component.ts
new file mode 100644
index 000000000..c8005536c
--- /dev/null
+++ b/src/app/pages/upload-image/upload-image.component.ts
@@ -0,0 +1,72 @@
+import { Component, Output, Input, EventEmitter } from '@angular/core';
+import { AngularFireStorage, AngularFireUploadTask } from 'angularfire2/storage';
+import { AngularFirestore } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+@Component({
+ selector: 'upload-image',
+ templateUrl: './upload-image.component.html',
+ styleUrls: ['./upload-image.component.scss']
+})
+export class UploadImageComponent {
+
+ @Input() username: string;
+ @Output() exit = new EventEmitter();
+ @Output() updateUser = new EventEmitter();
+
+ // Main task
+ task: AngularFireUploadTask;
+
+ // State for dropzone CSS toggling
+ isHovering: boolean;
+
+ // Progress monitoring
+ percentage: Observable;
+
+ snapshot: Observable;
+
+ // constructor(private db: AngularFirestore) { }
+ constructor(private storage: AngularFireStorage, private db: AngularFirestore) { }
+
+ toggleHover(event: boolean) {
+ this.isHovering = event;
+ }
+
+ startUpload(event: FileList) {
+ // The File object
+ const file = event.item(0);
+
+ // Client-side validation example
+ if (!file || file.type.split('/')[0] !== 'image') {
+ console.error('unsupported file type :( ');
+ return;
+ }
+
+ // The storage path
+ const path = `avatars/${new Date().getTime()}_${file.name}`;
+
+ // Totally optional metadata
+ const customMetadata = { app: 'My AngularFire-powered PWA!' };
+
+ // const ref = this.storage.ref(path);
+ // this.task = ref.put(file, { customMetadata });
+ // The main task
+ this.task = this.storage.upload(path, file, { customMetadata });
+
+ // Progress monitoring
+ this.percentage = this.task.percentageChanges();
+ this.snapshot = this.task.snapshotChanges().pipe(
+ tap(snap => {
+ if (snap.bytesTransferred === snap.totalBytes) {
+ this.updateUser.emit(path);
+ }
+ })
+ );
+ }
+
+ // Determines if the upload task is active
+ isActive(snapshot) {
+ return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes;
+ }
+}
diff --git a/src/app/pipe/file-size.pipe.ts b/src/app/pipe/file-size.pipe.ts
new file mode 100644
index 000000000..3bc76a88d
--- /dev/null
+++ b/src/app/pipe/file-size.pipe.ts
@@ -0,0 +1,25 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];
+
+@Pipe({
+ name: 'fileSize'
+})
+export class FileSizePipe implements PipeTransform {
+
+ transform(sizeInBytes: number, longForm: boolean): string {
+ const units = longForm
+ ? FILE_SIZE_UNITS_LONG
+ : FILE_SIZE_UNITS;
+
+ let power = Math.round(Math.log(sizeInBytes) / Math.log(1024));
+ power = Math.min(power, units.length - 1);
+
+ const size = sizeInBytes / Math.pow(1024, power); // size in new units
+ const formattedSize = Math.round(size * 100) / 100; // keep up to 2 decimals
+ const unit = units[power];
+
+ return size ? `${formattedSize} ${unit}` : '0';
+ }
+}
diff --git a/src/app/pipe/filesize.pipe.ts b/src/app/pipe/filesize.pipe.ts
new file mode 100644
index 000000000..ddee650b4
--- /dev/null
+++ b/src/app/pipe/filesize.pipe.ts
@@ -0,0 +1,25 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+const FILE_SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+const FILE_SIZE_UNITS_LONG = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Pettabytes', 'Exabytes', 'Zettabytes', 'Yottabytes'];
+
+@Pipe({
+ name: 'filesize'
+})
+export class FilesizePipe implements PipeTransform {
+
+ transform(sizeInBytes: number, longForm: boolean): string {
+ const units = longForm
+ ? FILE_SIZE_UNITS_LONG
+ : FILE_SIZE_UNITS;
+
+ let power = Math.round(Math.log(sizeInBytes) / Math.log(1024));
+ power = Math.min(power, units.length - 1);
+
+ const size = sizeInBytes / Math.pow(1024, power); // size in new units
+ const formattedSize = Math.round(size * 100) / 100; // keep up to 2 decimals
+ const unit = units[power];
+
+ return size ? `${formattedSize} ${unit}` : '0';
+ }
+}
diff --git a/src/app/providers/conference-data.ts b/src/app/providers/conference-data.ts
index 9891292d4..e366ae67a 100644
--- a/src/app/providers/conference-data.ts
+++ b/src/app/providers/conference-data.ts
@@ -1,117 +1,86 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
-import { of } from 'rxjs';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
-
import { UserData } from './user-data';
+import { Track, Session, PartOfDay, Map } from '../models';
+import { SessionData } from './session-data';
+import { Storage } from '@ionic/storage';
@Injectable({
providedIn: 'root'
})
export class ConferenceData {
- data: any;
+ tracksCollection: AngularFirestoreCollection ;
+ trackDoc: AngularFirestoreDocument ;
+ tracks: Observable ;
+ track: Observable ;
- constructor(public http: HttpClient, public user: UserData) {}
+ sessionsCollection: AngularFirestoreCollection;
+ sessions: Observable ;
+ session: Observable ;
- load(): any {
- if (this.data) {
- return of(this.data);
- } else {
- return this.http
- .get('assets/data/data.json')
- .pipe(map(this.processData, this));
- }
- }
+ partsOfDayCollection: AngularFirestoreCollection;
+ partOfDayDoc: AngularFirestoreDocument;
+ partsOfDay: Observable;
- processData(data: any) {
- // just some good 'ol JS fun with objects and arrays
- // build up the data by linking speakers to sessions
- this.data = data;
-
- this.data.tracks = [];
-
- // loop through each day in the schedule
- this.data.schedule.forEach((day: any) => {
- // loop through each timeline group in the day
- day.groups.forEach((group: any) => {
- // loop through each session in the timeline group
- group.sessions.forEach((session: any) => {
- session.speakers = [];
- if (session.speakerNames) {
- session.speakerNames.forEach((speakerName: any) => {
- const speaker = this.data.speakers.find(
- (s: any) => s.name === speakerName
- );
- if (speaker) {
- session.speakers.push(speaker);
- speaker.sessions = speaker.sessions || [];
- speaker.sessions.push(session);
- }
- });
- }
-
- if (session.tracks) {
- session.tracks.forEach((track: any) => {
- if (this.data.tracks.indexOf(track) < 0) {
- this.data.tracks.push(track);
- }
- });
- }
- });
- });
- });
+ mapsCollection: AngularFirestoreCollection;
+ maps: Observable;
+
+ data: any;
- return this.data;
+ constructor(
+ public http: HttpClient,
+ public storage: Storage,
+ public userProvider: UserData,
+ public sessionProvider: SessionData,
+ private afs: AngularFirestore) {
+ this.tracksCollection = this.afs.collection(
+ 'tracks', ref => ref.orderBy('name', 'asc'));
+ this.partsOfDayCollection = this.afs.collection(
+ 'partsOfDay', ref => ref.orderBy('indexKey', 'asc'));
+ this.mapsCollection = this.afs.collection(
+ 'maps', ref => ref.orderBy('name', 'asc'));
}
- getTimeline(
- dayIndex: number,
- queryText = '',
- excludeTracks: any[] = [],
- segment = 'all'
- ) {
- return this.load().pipe(
- map((data: any) => {
- const day = data.schedule[dayIndex];
- day.shownSessions = 0;
-
- queryText = queryText.toLowerCase().replace(/,|\.|-/g, ' ');
- const queryWords = queryText.split(' ').filter(w => !!w.trim().length);
-
- day.groups.forEach((group: any) => {
- group.hide = true;
-
- group.sessions.forEach((session: any) => {
- // check if this session should show or not
- this.filterSession(session, queryWords, excludeTracks, segment);
-
- if (!session.hide) {
- // if this session is not hidden then this group should show
- group.hide = false;
- day.shownSessions++;
- }
- });
- });
+ getPartsOfDay(): Observable {
+ this.partsOfDay = this.partsOfDayCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as PartOfDay;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.partsOfDay ;
+ }
- return day;
- })
- );
+ getSessionInPeriod(start, end): Observable {
+ this.sessionsCollection = this.afs.collection(
+ 'sessions', ref => ref.where('date', '>=', start)
+ .where('date', '<=', end)
+ .orderBy('date', 'asc'));
+ this.sessions = this.sessionsCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Session;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.sessions ;
}
- filterSession(
- session: any,
- queryWords: string[],
- excludeTracks: any[],
- segment: string
- ) {
+ filterSession(session: any, options: any) {
let matchesQueryText = false;
- if (queryWords.length) {
+ if (options.queryText.length > 0) {
// of any query word is in the session name than it passes the query test
- queryWords.forEach((queryWord: string) => {
- if (session.name.toLowerCase().indexOf(queryWord) > -1) {
- matchesQueryText = true;
- }
- });
+ if (session.name.toLowerCase().indexOf(options.queryText) > -1) {
+ matchesQueryText = true;
+ }
} else {
// if there are no query words then this session passes the query test
matchesQueryText = true;
@@ -121,7 +90,7 @@ export class ConferenceData {
// exclude tracks then this session passes the track test
let matchesTracks = false;
session.tracks.forEach((trackName: string) => {
- if (excludeTracks.indexOf(trackName) === -1) {
+ if (options.excludeTracks.indexOf(trackName) === -1) {
matchesTracks = true;
}
});
@@ -129,43 +98,92 @@ export class ConferenceData {
// if the segement is 'favorites', but session is not a user favorite
// then this session does not pass the segment test
let matchesSegment = false;
- if (segment === 'favorites') {
- if (this.user.hasFavorite(session.name)) {
- matchesSegment = true;
- }
+ if (options.segment === 'favorites') {
+ this.userProvider.getUser().then(user => {
+ matchesSegment = (user.favorites.findIndex(f => f.name === session.name) > -1);
+ // all tests must be true if it should not be hidden
+ session.hide = !(matchesQueryText && matchesTracks && matchesSegment);
+ });
} else {
- matchesSegment = true;
+ // doesn't matter about favorites.
+ session.hide = !(matchesQueryText && matchesTracks);
}
-
- // all tests must be true if it should not be hidden
- session.hide = !(matchesQueryText && matchesTracks && matchesSegment);
+ return session;
}
- getSpeakers() {
- return this.load().pipe(
- map((data: any) => {
- return data.speakers.sort((a: any, b: any) => {
- const aName = a.name.split(' ').pop();
- const bName = b.name.split(' ').pop();
- return aName.localeCompare(bName);
+ getTracks(): Observable {
+ this.tracks = this.tracksCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Track;
+ data.id = action.payload.doc.id;
+ return data;
});
- })
- );
+ }));
+ return this.tracks ;
}
- getTracks() {
- return this.load().pipe(
- map((data: any) => {
- return data.tracks.sort();
- })
- );
+ addTrack(track: Track) {
+ this.tracksCollection.add(track).then(res => {
+ this.userProvider.addTrackInUser(track.name);
+ });
+ }
+
+ updateTrack(track: Track, newName: string) {
+ const oldName = track.name;
+ track.name = newName;
+ const id = track.id;
+ delete(track.id);
+ this.trackDoc = this.afs.doc(`tracks/${id}`);
+ this.trackDoc.update(track).then(() => {
+ this.sessionProvider.updateTrackInSessions(newName, oldName);
+ this.userProvider.updateTracksInUser(newName, oldName);
+ });
+ }
+
+ removeTrack(track: Track) {
+ const id = track.id;
+ this.trackDoc = this.afs.doc(`tracks/${id}`);
+ this.trackDoc.delete().then(() => {
+ this.sessionProvider.removeTrackInSession(track.name);
+ this.userProvider.removeTrackInUser(track.name);
+ });
+ }
+
+ updatePartOfDay(pod: PartOfDay) {
+ const id = pod.id;
+ delete(pod.id);
+ this.partOfDayDoc = this.afs.doc(`partsOfDay/${id}`);
+ this.partOfDayDoc.update(pod);
+ }
+
+ changePartsOfDay(PODs: PartOfDay[], newPODs: PartOfDay[]) {
+ PODs.forEach(pod => {
+ const podDoc = this.afs.doc(`partsOfDay/${pod.id}`);
+ podDoc.delete();
+ });
+
+ newPODs.forEach(pod => {
+ this.partsOfDayCollection.add(pod);
+ });
+ }
+
+ getPeriod(): Promise {
+ return this.storage.get('period');
+ }
+
+ setPeriod(date: any): Promise {
+ return this.storage.set('period', date);
}
getMap() {
- return this.load().pipe(
- map((data: any) => {
- return data.map;
- })
- );
+ this.maps = this.mapsCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Map;
+ return data;
+ });
+ }));
+ return this.maps ;
}
}
diff --git a/src/app/providers/function-data.ts b/src/app/providers/function-data.ts
new file mode 100644
index 000000000..e07459138
--- /dev/null
+++ b/src/app/providers/function-data.ts
@@ -0,0 +1,95 @@
+import { Injectable } from '@angular/core';
+import { AlertController } from '@ionic/angular';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class FunctionlData {
+
+ constructor(private alertCtrl: AlertController) {}
+
+ async onError(header: string, message: string) {
+ const alert = await this.alertCtrl.create({
+ header: header,
+ message: message,
+ buttons: [
+ {
+ text: 'ok',
+ role: 'cancel',
+ handler: () => {
+ }
+ }
+ ],
+ backdropDismiss: false
+ });
+ await alert.present();
+ }
+
+ getDateFormat(date?: Date) {
+ if (!date) {
+ date = new Date();
+ }
+ const dateArray = date.toLocaleDateString().split('/');
+ return dateArray[2] + '-' +
+ this.reform2digits(dateArray[0]) + '-' +
+ this.reform2digits(dateArray[1]);
+ }
+
+ // change date format : USA <-> EU
+ changeDateFormat(date: string) {
+ const arr = date.split('-');
+ if (arr[0].length > 3) {
+ return `${arr[1]}-${arr[2]}-${arr[0]}`;
+ }
+ return `${arr[2]}-${arr[0]}-${arr[1]}`;
+ }
+
+ getAmPmTimeFormat(time: string): string {
+ if (!time) { return '00:00 am' ; }
+ let t = +time.split(':')[0];
+ let m = time.split(':')[1];
+ if (t > 11) {
+ m = m + ' pm';
+ t = (t === 12) ? t : t - 12 ;
+ } else {
+ m = m + ' am';
+ }
+ return this.reform2digits(t) + ':' + m ;
+ }
+
+ get24HoursFormat(time: string): string {
+ if (!time) { return '' ; }
+ const data = time.split(' ');
+ let t = +data[0].split(':')[0];
+ const m = data[0].split(':')[1];
+ if (data[1] === 'pm' && t < 12) { t = t + 12 ; }
+ const realTime = (t < 10 ? '0' : '') + t;
+ return realTime + ':' + m ;
+ }
+
+ reform2digits(value): string {
+ value = +value;
+ return ((value < 10) ? '0' : '') + value ;
+ }
+
+ addMinute(time) {
+ const [h, m] = time.split(':');
+ const hour = this.reform2digits((m === '59') ? +h + 1 : +h);
+ const min = this.reform2digits((m === '59') ? 0 : +m + 1);
+ return (hour === '24') ? null : hour + ':' + min ;
+ }
+
+ checkDateValidation(s_date: string): boolean {
+ const months = [1, 3, 5, 7, 8, 10, 12];
+ const arr = s_date.split('-');
+ const year = +arr[0];
+ const month = +arr[1];
+ const date = +arr[2];
+ if (months.find(num => num === month)) { return date < 32; }
+ if (month > 2) { return date < 31; }
+ if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
+ return date < 30;
+ }
+ return date < 29;
+ }
+}
diff --git a/src/app/providers/session-data.ts b/src/app/providers/session-data.ts
new file mode 100644
index 000000000..d3a77313a
--- /dev/null
+++ b/src/app/providers/session-data.ts
@@ -0,0 +1,105 @@
+import { Injectable } from '@angular/core';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { Session } from '../models';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SessionData {
+ sessionsCollection: AngularFirestoreCollection ;
+ sessionDoc: AngularFirestoreDocument ;
+ sessions: Observable ;
+ session: Observable ;
+ id: string;
+
+ constructor(private afs: AngularFirestore) {}
+
+ getSessionsInPeriod(start, end): Observable {
+ this.sessionsCollection = this.afs.collection(
+ 'sessions', ref => ref.where('date', '>=', start)
+ .where('date', '<=', end)
+ .orderBy('date', 'asc'));
+ this.sessions = this.sessionsCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Session;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.sessions ;
+ }
+
+ getSessions(): Observable {
+ this.sessionsCollection = this.afs.collection(
+ 'sessions', ref => ref.orderBy('date', 'asc'));
+ this.sessions = this.sessionsCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Session;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.sessions ;
+ }
+
+ addNewSession(session: Session) {
+ return this.sessionsCollection.add(session)
+ .then(docRef => {
+ const id = docRef.id;
+ return id;
+ });
+ }
+
+ updateTrackInSessions(newName: string, oldName: string) {
+ this.getSessions().subscribe(sessions => {
+ sessions.forEach(session => {
+ const idx = session.tracks.findIndex(track => track === oldName);
+ if (idx > -1) {
+ session.tracks[idx] = newName;
+ this.updateSession(session);
+ }
+ });
+ });
+ }
+
+ removeTrackInSession(name: string) {
+ this.getSessions().subscribe(sessions => {
+ sessions.forEach(session => {
+ const idx = session.tracks.findIndex(track => track === name);
+ if (idx > -1) {
+ session.tracks.splice(idx, 1);
+ this.updateSession(session);
+ }
+ });
+ });
+ }
+
+ updateSession(session: Session) {
+ const id = session.id;
+ delete(session.id);
+ this.sessionDoc = this.afs.doc(`sessions/${id}`);
+ this.sessionDoc.update(session);
+ session.id = id;
+ }
+
+ removeSession(session: Session) {
+ this.sessionDoc = this.afs.doc(`sessions/${session.id}`);
+ this.sessionDoc.delete();
+ }
+
+ getSession(id: string): Promise {
+ return this.sessionsCollection.doc(id).ref.get()
+ .then(doc => {
+ const session = doc.data() as Session ;
+ session.id = id;
+ return session;
+ });
+ }
+}
diff --git a/src/app/providers/speaker-data.ts b/src/app/providers/speaker-data.ts
new file mode 100644
index 000000000..37756406c
--- /dev/null
+++ b/src/app/providers/speaker-data.ts
@@ -0,0 +1,120 @@
+import { Injectable } from '@angular/core';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { AngularFireStorage } from 'angularfire2/storage';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { Speaker } from '../models';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SpeakerData {
+ speakersCollection: AngularFirestoreCollection ;
+ speakerDoc: AngularFirestoreDocument ;
+ speakers: Observable ;
+ speaker: Observable ;
+
+ constructor(private afs: AngularFirestore,
+ private fireStorage: AngularFireStorage) {
+ this.speakersCollection = this.afs.collection(
+ 'speakers', ref => ref.orderBy('name', 'asc'));
+ }
+
+ getSpeakers(): Observable {
+ this.speakers = this.speakersCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const speaker = action.payload.doc.data() as Speaker;
+ speaker.id = action.payload.doc.id;
+ if (speaker.profilePic) {
+ this.fireStorage.ref(speaker.profilePic).getDownloadURL().subscribe(url => {
+ speaker.profilePic = url ? url : '';
+ });
+ }
+ return speaker;
+ });
+ }));
+ return this.speakers ;
+ }
+
+ getSpeaker(id: string) {
+ return this.speakersCollection.doc(id).ref.get()
+ .then(doc => {
+ const speaker = doc.data() as Speaker;
+ if (speaker.profilePic) {
+ this.fireStorage.ref(speaker.profilePic).getDownloadURL().subscribe(url => {
+ speaker.profilePic = url;
+ });
+ }
+ return speaker;
+ });
+ }
+
+ addNewSpeaker(speaker: Speaker) {
+ console.log(speaker);
+ this.speakersCollection.add(speaker) ;
+ }
+
+ removeSpeaker(speaker: Speaker) {
+ const id = speaker.id;
+ this.speakerDoc = this.afs.doc(`speakers/${id}`);
+ this.speakerDoc.delete();
+ }
+
+ updateSpeaker(speaker: Speaker) {
+ const id = speaker.id;
+ this.speakerDoc = this.afs.doc(`speakers/${id}`);
+ delete(speaker.id);
+ this.speakerDoc.update(speaker);
+ speaker.id = id;
+ }
+
+ getSpeakerName(id: string) {
+ this.speakerDoc = this.afs.doc(`speakers/${id}`);
+ const name = this.speakerDoc.snapshotChanges.name;
+ return name;
+ }
+
+ updateSpeakers() {
+ this.getSpeakers().subscribe(
+ (speakers: Speaker[]) => {
+ speakers.forEach( speaker => {
+ const id = speaker.id;
+ delete(speaker.id);
+ speaker.instagram = '';
+ speaker.github = 'hogusong';
+ if (!speaker.twitter) { speaker.twitter = 'YoungSongJS'; }
+ this.speakerDoc = this.afs.doc(`speakers/${id}`);
+ this.speakerDoc.update(speaker);
+ });
+ }
+ );
+ }
+
+ getUrl(path): Observable {
+ return this.fireStorage.ref(path).getDownloadURL();
+ }
+
+ deleteUrl(oldUrl) {
+ if (oldUrl) {
+ this.fireStorage.storage.refFromURL(oldUrl).delete();
+ }
+ }
+
+ getSpeakerById(id: string) {
+ return this.speakersCollection.doc(id).ref.get()
+ .then(doc => {
+ const speaker = doc.data() as Speaker;
+ speaker.id = id;
+ if (speaker.profilePic) {
+ this.fireStorage.ref(speaker.profilePic).getDownloadURL().subscribe(url => {
+ speaker.profilePic = url;
+ });
+ }
+ return speaker;
+ });
+ }
+}
diff --git a/src/app/providers/support-data.ts b/src/app/providers/support-data.ts
new file mode 100644
index 000000000..4168b2520
--- /dev/null
+++ b/src/app/providers/support-data.ts
@@ -0,0 +1,54 @@
+import { Injectable } from '@angular/core';
+import { Events } from '@ionic/angular';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { Support } from '../models';
+import { FunctionlData } from './function-data';
+import { UserData } from './user-data';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SupportData {
+ supportsCollection: AngularFirestoreCollection ;
+ supportDoc: AngularFirestoreDocument ;
+ supports: Observable ;
+ support: Observable ;
+
+ constructor(private afs: AngularFirestore,
+ private functionProvider: FunctionlData,
+ private userProvider: UserData,
+ public events: Events) {
+ this.supportsCollection = this.afs.collection(
+ 'supports', ref => ref.orderBy('date', 'asc'));
+ }
+
+ getSupports(): Observable {
+ this.supports = this.supportsCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as Support;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.supports;
+ }
+
+ addSupport(message: string) {
+ const date = this.functionProvider.getDateFormat(null);
+ this.userProvider.getUserId()
+ .then(id => {
+ const support: Support = {
+ date: date,
+ userId: id,
+ support: message
+ };
+ this.supportsCollection.add(support);
+ });
+ }
+}
diff --git a/src/app/providers/user-data.ts b/src/app/providers/user-data.ts
index c171627b1..2c186e8b1 100644
--- a/src/app/providers/user-data.ts
+++ b/src/app/providers/user-data.ts
@@ -1,20 +1,53 @@
import { Injectable } from '@angular/core';
import { Events } from '@ionic/angular';
import { Storage } from '@ionic/storage';
+import { AngularFirestore,
+ AngularFirestoreCollection,
+ AngularFirestoreDocument } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { User } from '../models';
+import { AngularFireStorage } from 'angularfire2/storage';
@Injectable({
providedIn: 'root'
})
export class UserData {
+ usersCollection: AngularFirestoreCollection ;
+ userDoc: AngularFirestoreDocument ;
+ users: Observable ;
_favorites: string[] = [];
HAS_LOGGED_IN = 'hasLoggedIn';
HAS_SEEN_TUTORIAL = 'hasSeenTutorial';
- constructor(
- public events: Events,
- public storage: Storage
- ) { }
+ constructor(private afs: AngularFirestore,
+ private fireStorage: AngularFireStorage,
+ public events: Events,
+ public storage: Storage) {
+ this.usersCollection = this.afs.collection(
+ 'users', ref => ref.orderBy('username', 'asc'));
+ }
+
+ getUsers(): Observable {
+ this.users = this.usersCollection.snapshotChanges()
+ .pipe(map(response => {
+ return response.map(action => {
+ const data = action.payload.doc.data() as User;
+ data.id = action.payload.doc.id;
+ return data;
+ });
+ }));
+ return this.users;
+ }
+
+ signup(user: User) {
+ this.usersCollection.add(user)
+ .then(res => {
+ user.id = res.id;
+ this.login(user);
+ });
+ }
hasFavorite(sessionName: string): boolean {
return (this._favorites.indexOf(sessionName) > -1);
@@ -31,47 +64,108 @@ export class UserData {
}
}
- login(username: string): Promise {
+ login(user: User): Promise {
return this.storage.set(this.HAS_LOGGED_IN, true).then(() => {
- this.setUsername(username);
+ this.setUser(user);
return this.events.publish('user:login');
});
}
- signup(username: string): Promise {
- return this.storage.set(this.HAS_LOGGED_IN, true).then(() => {
- this.setUsername(username);
- return this.events.publish('user:signup');
- });
- }
-
logout(): Promise {
return this.storage.remove(this.HAS_LOGGED_IN).then(() => {
- return this.storage.remove('username');
+ return this.storage.remove('user');
}).then(() => {
this.events.publish('user:logout');
});
}
- setUsername(username: string): Promise {
- return this.storage.set('username', username);
+ setUser(user: User): Promise {
+ return this.storage.set('user', user);
}
- getUsername(): Promise {
- return this.storage.get('username').then((value) => {
- return value;
+ updateUser(user: User) {
+ const id = user.id;
+ delete(user.id);
+ this.userDoc = this.afs.doc(`users/${id}`);
+ this.userDoc.update(user);
+ user.id = id;
+ this.setUser(user);
+ }
+
+ addTrackInUser(name) {
+ const track = { name: name, isChecked: false };
+ this.getUsers().subscribe(users => {
+ users.forEach(user => {
+ const idx = user.trackFilter.findIndex(item => item.name === name);
+ if (idx < 0) {
+ user.trackFilter.push(track);
+ this.updateUser(user);
+ }
+ });
});
}
- isLoggedIn(): Promise {
- return this.storage.get(this.HAS_LOGGED_IN).then((value) => {
- return value === true;
+ updateTracksInUser(newName: string, oldName: string) {
+ this.getUsers().subscribe(users => {
+ users.forEach(user => {
+ const idx = user.trackFilter.findIndex(track => track.name === oldName);
+ if (idx > -1) {
+ user.trackFilter[idx].name = newName;
+ this.updateUser(user);
+ }
+ });
});
}
- checkHasSeenTutorial(): Promise {
- return this.storage.get(this.HAS_SEEN_TUTORIAL).then((value) => {
- return value;
+ removeTrackInUser(name) {
+ this.getUsers().subscribe(users => {
+ users.forEach(user => {
+ const idx = user.trackFilter.findIndex(track => track.name === name);
+ if (idx > -1) {
+ user.trackFilter.splice(idx, 1);
+ this.updateUser(user);
+ }
+ });
});
}
+
+ deleteUrl(oldUrl) {
+ if (oldUrl) {
+ this.fireStorage.storage.refFromURL(oldUrl).delete();
+ }
+ }
+
+ getUserById(id: string) {
+ return this.usersCollection.doc(id).ref.get()
+ .then(doc => {
+ const user = doc.data() as User;
+ user.id = id;
+ if (user.avatar) {
+ this.fireStorage.ref(user.avatar).getDownloadURL().subscribe(url => {
+ user.avatar = url;
+ });
+ }
+ return user;
+ });
+ }
+
+ getUser(): Promise {
+ return this.storage.get('user').then(user => user);
+ }
+
+ getUsername(): Promise {
+ return this.storage.get('user').then(value => value.username);
+ }
+
+ getUserId(): Promise {
+ return this.storage.get('user').then(value => value.id);
+ }
+
+ isLoggedIn(): Promise {
+ return this.storage.get(this.HAS_LOGGED_IN).then(value => value);
+ }
+
+ checkHasSeenTutorial(): Promise {
+ return this.storage.get(this.HAS_SEEN_TUTORIAL).then(value => value);
+ }
}
diff --git a/src/app/setup/map/map-setup.html b/src/app/setup/map/map-setup.html
new file mode 100644
index 000000000..ab591f34e
--- /dev/null
+++ b/src/app/setup/map/map-setup.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Map
+
+
+
+
+
+
diff --git a/src/app/setup/map/map-setup.scss b/src/app/setup/map/map-setup.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/map/map-setup.ts b/src/app/setup/map/map-setup.ts
new file mode 100644
index 000000000..3315b18e3
--- /dev/null
+++ b/src/app/setup/map/map-setup.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit } from '@angular/core';
+import { UserData } from '../../providers/user-data';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'map-setup',
+ templateUrl: './map-setup.html',
+ styleUrls: ['./map-setup.scss'],
+})
+export class MapSetup implements OnInit {
+
+ constructor(private userProvider: UserData,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.userProvider.getUser().then(user => {
+ if ( !user || user.username !== 'admin') {
+ this.router.navigateByUrl('/tutorial');
+ }
+ });
+ }
+
+}
diff --git a/src/app/setup/map/map.module.ts b/src/app/setup/map/map.module.ts
new file mode 100644
index 000000000..41e2acdb7
--- /dev/null
+++ b/src/app/setup/map/map.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { MapSetup } from './map-setup';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: MapSetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [MapSetup]
+})
+export class MapSetupModule {}
diff --git a/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.module.ts b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.module.ts
new file mode 100644
index 000000000..197c5dcdc
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { PartOfDayNewPage } from './part-of-day-new.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: PartOfDayNewPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [PartOfDayNewPage]
+})
+export class PartOfDayNewPageModule {}
diff --git a/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.html b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.html
new file mode 100644
index 000000000..c0267f657
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ New Set of POD
+
+
+
+
+
+
+ Description:
+
+
+
+
+ From {{ timeFrom }}
+ To
+
+
+
+ Cancel
+ Add POD
+
+
+
+
+
+
+
+
+
+
+ {{ POD.indexKey }}
+
+
+ {{ POD.name }}
+
+
+ {{ POD.timeFrom }}
+
+
+ {{ POD.timeTo }}
+
+
+
+
diff --git a/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.scss b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.scss
new file mode 100644
index 000000000..526d594a3
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.scss
@@ -0,0 +1,8 @@
+.table-header, .table-item {
+ display: grid;
+ grid-template-columns: 1fr 3fr 1fr 1fr;
+
+ ion-col {
+ text-align: left;
+ }
+}
diff --git a/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.ts b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.ts
new file mode 100644
index 000000000..fa9b58c08
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-new/part-of-day-new.page.ts
@@ -0,0 +1,115 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { PartOfDay } from '../../../models';
+import { FunctionlData } from '../../../providers/function-data';
+import { ConferenceData } from '../../../providers/conference-data';
+import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular';
+import { UserData } from '../../../providers/user-data';
+
+@Component({
+ selector: 'part-of-day-new',
+ templateUrl: './part-of-day-new.page.html',
+ styleUrls: ['./part-of-day-new.page.scss'],
+})
+export class PartOfDayNewPage implements OnInit {
+ header = 'Part Of Day';
+ PODs: PartOfDay[] = [];
+ newPODs: PartOfDay[] = [];
+ title = '';
+ timeFrom: string;
+ timeTo: string;
+ len: number;
+
+ constructor(private cdRef: ChangeDetectorRef,
+ private alertCtrl: AlertController,
+ private funProvider: FunctionlData,
+ private userProvider: UserData,
+ private confProvider: ConferenceData,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.userProvider.getUser().then(user => {
+ if ( !user || user.username !== 'admin') {
+ this.router.navigateByUrl('/tutorial');
+ } else {
+ this.confProvider.getPartsOfDay().subscribe(data => this.PODs = data);
+ this.setInitialValue();
+ }
+ });
+ }
+
+ setInitialValue() {
+ this.title = '';
+ this.len = this.newPODs.length;
+ if (this.len === 0) {
+ this.timeFrom = '00:00';
+ } else {
+ this.timeFrom = this.funProvider.addMinute(this.newPODs[this.len - 1].timeTo);
+ }
+ this.timeTo = this.funProvider.addMinute(this.timeFrom);
+ }
+
+ cancelInput() {
+ this.setInitialValue();
+ }
+
+ saveInput() {
+ if (this.isTheValueUsed()) {
+ this.funProvider.onError(this.header, 'Description is not valid. Try again');
+ } else {
+ const newPOD = {
+ indexKey: this.newPODs.length + 1,
+ name: this.title,
+ timeFrom: this.timeFrom,
+ timeTo: this.timeTo
+ } as PartOfDay;
+ this.newPODs.push(newPOD);
+ if (this.timeTo === '23:59') {
+ this.presentConfirm();
+ } else {
+ this.setInitialValue();
+ }
+ }
+ }
+
+ isTheValueUsed() {
+ this.title = this.title.trim();
+ if (this.title === '') {
+ return true;
+ }
+ return this.PODs.find(pod => pod.name.toLowerCase() === this.title.toLowerCase());
+ }
+
+ checkTimeTo(value) {
+ this.cdRef.detectChanges();
+ if (value <= this.timeFrom) {
+ this.funProvider.onError(this.header, 'Wrong time for To. Try again.');
+ this.timeTo = this.funProvider.addMinute(this.timeFrom);
+ }
+ }
+
+ async presentConfirm() {
+ const alert = await this.alertCtrl.create({
+ header: 'Confirm Save',
+ message: 'Done the job. Do you want to save it?',
+ buttons: [
+ {
+ text: 'Cancel',
+ role: 'cancel',
+ handler: () => {
+ this.router.navigateByUrl('setup/tabs/(partofday:partofday)');
+ }
+ },
+ {
+ text: 'Save',
+ handler: () => {
+ this.confProvider.changePartsOfDay(this.PODs, this.newPODs);
+ this.router.navigateByUrl('setup/tabs/(partofday:partofday)');
+ }
+ }
+ ],
+ backdropDismiss: false
+ });
+ await alert.present();
+ }
+}
diff --git a/src/app/setup/part-of-day/part-of-day-setup.html b/src/app/setup/part-of-day/part-of-day-setup.html
new file mode 100644
index 000000000..3f5b99a4a
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-setup.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+ Part of Day
+
+ Create New Set
+
+
+
+
+
+
+
+
+
+
+ {{ POD.indexKey }}
+
+
+ {{ POD.name }}
+
+
+ {{ POD.timeFrom }}
+
+
+ {{ POD.timeTo }}
+
+
+ Edit
+
+
+
+
diff --git a/src/app/setup/part-of-day/part-of-day-setup.scss b/src/app/setup/part-of-day/part-of-day-setup.scss
new file mode 100644
index 000000000..74584060e
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-setup.scss
@@ -0,0 +1,8 @@
+.table-header, .table-item {
+ display: grid;
+ grid-template-columns: 1fr 3fr 1fr 1fr 1fr;
+
+ ion-col {
+ text-align: left;
+ }
+}
diff --git a/src/app/setup/part-of-day/part-of-day-setup.ts b/src/app/setup/part-of-day/part-of-day-setup.ts
new file mode 100644
index 000000000..a773951da
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day-setup.ts
@@ -0,0 +1,66 @@
+import { Component, OnInit } from '@angular/core';
+import { PartOfDay } from '../../models';
+import { ConferenceData } from '../../providers/conference-data';
+import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular';
+import { FunctionlData } from '../../providers/function-data';
+
+@Component({
+ selector: 'part-of-day',
+ templateUrl: './part-of-day-setup.html',
+ styleUrls: ['./part-of-day-setup.scss'],
+})
+export class PartOfDaySetup implements OnInit {
+
+ PODs: PartOfDay[];
+
+ constructor(private confData: ConferenceData,
+ private alertCtrl: AlertController,
+ private funProvider: FunctionlData,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.confData.getPartsOfDay().subscribe(data => {
+ this.PODs = data;
+ });
+ }
+
+ makeNewPOD() {
+ this.router.navigateByUrl('setup/tabs/(partofday:new)');
+ }
+
+ async editTitle(pod) {
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Description',
+ subHeader: pod.name,
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ data.newName = data.newName.trim();
+ if (this.isTheValueUsed(data.newName)) {
+ this.funProvider.onError('Part Of Day', data.newName + ' was used already. Try another.');
+ } else {
+ pod.name = data.newName;
+ this.confData.updatePartOfDay(pod);
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'text',
+ name: 'newName',
+ placeholder: 'new descrition here'
+ }
+ ],
+ backdropDismiss: false
+ });
+ await changeForm.present();
+ }
+
+ isTheValueUsed(name) {
+ return this.PODs.find(pod => pod.name.toLowerCase() === name.toLowerCase());
+ }
+}
diff --git a/src/app/setup/part-of-day/part-of-day.module.ts b/src/app/setup/part-of-day/part-of-day.module.ts
new file mode 100644
index 000000000..26e3de437
--- /dev/null
+++ b/src/app/setup/part-of-day/part-of-day.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { PartOfDaySetup } from './part-of-day-setup';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: PartOfDaySetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes),
+ ],
+ declarations: [PartOfDaySetup]
+})
+export class PartOfDaySetupModule {}
diff --git a/src/app/setup/period/period.module.ts b/src/app/setup/period/period.module.ts
new file mode 100644
index 000000000..754aec4db
--- /dev/null
+++ b/src/app/setup/period/period.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { PeriodPage } from './period.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: PeriodPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [PeriodPage]
+})
+export class PeriodPageModule {}
diff --git a/src/app/setup/period/period.page.html b/src/app/setup/period/period.page.html
new file mode 100644
index 000000000..bea46c525
--- /dev/null
+++ b/src/app/setup/period/period.page.html
@@ -0,0 +1,31 @@
+
+
+ Date Period
+
+ Cancel
+ Save Period
+
+
+
+
+
+
+ Enter new Period to make Schedule.
+
+
+
+ From :
+
+
+
+ To :
+
+
+
+
diff --git a/src/app/setup/period/period.page.scss b/src/app/setup/period/period.page.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/period/period.page.ts b/src/app/setup/period/period.page.ts
new file mode 100644
index 000000000..e9201b65e
--- /dev/null
+++ b/src/app/setup/period/period.page.ts
@@ -0,0 +1,57 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { NavParams, ModalController } from '@ionic/angular';
+import { FunctionlData } from '../../providers/function-data';
+
+@Component({
+ selector: 'period',
+ templateUrl: './period.page.html',
+ styleUrls: ['./period.page.scss'],
+})
+export class PeriodPage implements OnInit {
+
+ start: string;
+ end: string;
+ minYear: string;
+ maxYear: string;
+
+ constructor(private navParams: NavParams,
+ private modalCtrl: ModalController,
+ private cdRef: ChangeDetectorRef,
+ private funProvider: FunctionlData) { }
+
+ ngOnInit() {
+ this.start = this.navParams.get('start');
+ this.end = this.navParams.get('end');
+ this.minYear = '' + (+this.start.substring(0, 4) - 20);
+ this.maxYear = '' + (+this.end.substring(0, 4) + 20);
+ }
+
+ checkStartDate(value) {
+ this.cdRef.detectChanges();
+ if (this.funProvider.checkDateValidation(value)) {
+ this.end = (this.end < value) ? value : this.end;
+ } else {
+ this.funProvider.onError('Confirm Date', 'The date is not valid. Try again.');
+ this.start = this.navParams.get('start');
+ }
+ }
+
+ checkEndDate(value) {
+ this.cdRef.detectChanges();
+ if (!this.funProvider.checkDateValidation(value)) {
+ this.funProvider.onError('Confirm Date', 'The date is not valid. Try again.');
+ this.end = this.start;
+ } else if (this.start > value) {
+ this.funProvider.onError('Confirm Date', 'The end of period is wrong. Try again.');
+ this.end = this.start;
+ }
+ }
+
+ applySelection() {
+ this.modalCtrl.dismiss({ start: this.start, end: this.end });
+ }
+
+ onExit() {
+ this.modalCtrl.dismiss(null);
+ }
+}
diff --git a/src/app/setup/sessions/pick-speakers/pick-speakers.module.ts b/src/app/setup/sessions/pick-speakers/pick-speakers.module.ts
new file mode 100644
index 000000000..d2da49881
--- /dev/null
+++ b/src/app/setup/sessions/pick-speakers/pick-speakers.module.ts
@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { PickSpeakersPage } from './pick-speakers.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: PickSpeakersPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+})
+export class PickSpeakersPageModule {}
diff --git a/src/app/setup/sessions/pick-speakers/pick-speakers.page.html b/src/app/setup/sessions/pick-speakers/pick-speakers.page.html
new file mode 100644
index 000000000..cc94c465a
--- /dev/null
+++ b/src/app/setup/sessions/pick-speakers/pick-speakers.page.html
@@ -0,0 +1,35 @@
+
+
+ Select Speakers
+
+
+ Cancel
+ Save
+
+
+
+
+
+
+
+
+
+
+
+
+ Speakers
+
+
+
+
+ {{ spk.name }} ( {{ spk.phone }} )
+
+
+
+
+
+
+ Unselect All Speakers
+
+
+
diff --git a/src/app/setup/sessions/pick-speakers/pick-speakers.page.scss b/src/app/setup/sessions/pick-speakers/pick-speakers.page.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/sessions/pick-speakers/pick-speakers.page.ts b/src/app/setup/sessions/pick-speakers/pick-speakers.page.ts
new file mode 100644
index 000000000..1b85edc22
--- /dev/null
+++ b/src/app/setup/sessions/pick-speakers/pick-speakers.page.ts
@@ -0,0 +1,53 @@
+import { Component, OnInit } from '@angular/core';
+import { NavParams, ModalController } from '@ionic/angular';
+
+@Component({
+ selector: 'pick-speakers',
+ templateUrl: './pick-speakers.page.html',
+ styleUrls: ['./pick-speakers.page.scss'],
+})
+export class PickSpeakersPage implements OnInit {
+
+ queryText = '';
+ s_speakers: {
+ id: string, name: string, phone: string,
+ isChecked: boolean
+ }[] = [];
+
+ constructor(private navParams: NavParams,
+ private modalCtrl: ModalController) { }
+
+ ngOnInit() {
+ const spks = this.navParams.get('speakers');
+ const IDs = this.navParams.get('ids');
+ spks.forEach(spk => {
+ const idx = IDs.findIndex(id => id === spk.id);
+ if (idx > -1) {
+ this.s_speakers.push({ isChecked: true, id: spk.id, name: spk.name, phone: spk.phone });
+ } else {
+ this.s_speakers.push({ isChecked: false, id: spk.id, name: spk.name, phone: spk.phone });
+ }
+ });
+ }
+
+ unselectAll() {
+ this.s_speakers.forEach(spk => { spk.isChecked = false; });
+ }
+
+ applySelection() {
+ const IDs = [];
+ this.s_speakers.forEach(spk => {
+ if (spk.isChecked) {
+ IDs.push(spk.id);
+ }
+ });
+ this.dismiss(IDs);
+ }
+
+ dismiss(data: any) {
+ // using the injected ModalController this page
+ // can "dismiss" itself and pass back data
+ this.modalCtrl.dismiss(data);
+ }
+
+}
diff --git a/src/app/setup/sessions/pick-tracks/pick-tracks.module.ts b/src/app/setup/sessions/pick-tracks/pick-tracks.module.ts
new file mode 100644
index 000000000..6f2983d1f
--- /dev/null
+++ b/src/app/setup/sessions/pick-tracks/pick-tracks.module.ts
@@ -0,0 +1,25 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { PickTracksPage } from './pick-tracks.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: PickTracksPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+})
+export class PickTracksPageModule {}
diff --git a/src/app/setup/sessions/pick-tracks/pick-tracks.page.html b/src/app/setup/sessions/pick-tracks/pick-tracks.page.html
new file mode 100644
index 000000000..87a6029b8
--- /dev/null
+++ b/src/app/setup/sessions/pick-tracks/pick-tracks.page.html
@@ -0,0 +1,36 @@
+
+
+ Select Tracks
+
+
+ Cancel
+ Save
+
+
+
+
+
+
+
+
+
+
+
+
+ Tracks
+
+
+
+
+ {{ tk.name }}
+
+
+
+
+
+
+ Unselect All Tracks
+
+
+
+
\ No newline at end of file
diff --git a/src/app/setup/sessions/pick-tracks/pick-tracks.page.scss b/src/app/setup/sessions/pick-tracks/pick-tracks.page.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/sessions/pick-tracks/pick-tracks.page.ts b/src/app/setup/sessions/pick-tracks/pick-tracks.page.ts
new file mode 100644
index 000000000..ec0950da0
--- /dev/null
+++ b/src/app/setup/sessions/pick-tracks/pick-tracks.page.ts
@@ -0,0 +1,49 @@
+import { Component, OnInit } from '@angular/core';
+import { NavParams, ModalController } from '@ionic/angular';
+
+@Component({
+ selector: 'pick-tracks',
+ templateUrl: './pick-tracks.page.html',
+ styleUrls: ['./pick-tracks.page.scss'],
+})
+export class PickTracksPage implements OnInit {
+
+ queryText = '';
+ s_tracks: { name: string, isChecked: boolean }[] = [];
+
+ constructor(private navParams: NavParams,
+ private modalCtrl: ModalController) { }
+
+ ngOnInit() {
+ const tks = this.navParams.get('tracks');
+ const names = this.navParams.get('names');
+ tks.forEach(tk => {
+ const idx = names.findIndex(name => name === tk.name);
+ if (idx > -1) {
+ this.s_tracks.push({ isChecked: true, name: tk.name });
+ } else {
+ this.s_tracks.push({ isChecked: false, name: tk.name });
+ }
+ });
+ }
+
+ unselectAll() {
+ this.s_tracks.forEach(tk => { tk.isChecked = false; });
+ }
+
+ applySelection() {
+ const selected = [];
+ this.s_tracks.forEach(tk => {
+ if (tk.isChecked) {
+ selected.push(tk.name);
+ }
+ });
+ this.dismiss(selected);
+ }
+
+ dismiss(data: any) {
+ // using the injected ModalController this page
+ // can "dismiss" itself and pass back data
+ this.modalCtrl.dismiss(data);
+ }
+}
diff --git a/src/app/setup/sessions/session-edit/session-edit.module.ts b/src/app/setup/sessions/session-edit/session-edit.module.ts
new file mode 100644
index 000000000..2666de60c
--- /dev/null
+++ b/src/app/setup/sessions/session-edit/session-edit.module.ts
@@ -0,0 +1,27 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+import { IonicModule } from '@ionic/angular';
+
+import { SessionEditPage } from './session-edit.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SessionEditPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [
+ SessionEditPage
+ ]
+})
+export class SessionEditPageModule {}
diff --git a/src/app/setup/sessions/session-edit/session-edit.page.html b/src/app/setup/sessions/session-edit/session-edit.page.html
new file mode 100644
index 000000000..5e452b5ba
--- /dev/null
+++ b/src/app/setup/sessions/session-edit/session-edit.page.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+ {{mode}} Session
+
+ Cancel
+ Save
+
+
+
+
+
+
+
+ Title:
+
+
+
+ Date:
+
+
+
+ From:
+
+ To :
+
+
+
+ Location:
+
+
+
+ Description
+
+
+
+
+
+
+ Speakers:
+
+
+
+
+
+
+ {{ s.name }} ( {{ s.phone }} )
+
+
+
+
+
+
+
+ Tracks:
+
+
+
+
+
+
+ {{ t.name }}
+
+
+
+
+
+
+
diff --git a/src/app/setup/sessions/session-edit/session-edit.page.scss b/src/app/setup/sessions/session-edit/session-edit.page.scss
new file mode 100644
index 000000000..7284b7b90
--- /dev/null
+++ b/src/app/setup/sessions/session-edit/session-edit.page.scss
@@ -0,0 +1,11 @@
+.get-time {
+ width: 100px;
+}
+
+.get-date {
+ margin-right: 30px;
+}
+
+ion-textarea {
+ height: 210px;
+}
diff --git a/src/app/setup/sessions/session-edit/session-edit.page.ts b/src/app/setup/sessions/session-edit/session-edit.page.ts
new file mode 100644
index 000000000..3daaa1a6d
--- /dev/null
+++ b/src/app/setup/sessions/session-edit/session-edit.page.ts
@@ -0,0 +1,169 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+
+import { Session, Track, Speaker } from '../../../models';
+import { SessionData } from '../../../providers/session-data';
+import { Router, ActivatedRoute } from '@angular/router';
+import { FunctionlData } from '../../../providers/function-data';
+import { SpeakerData } from '../../../providers/speaker-data';
+import { ConferenceData } from '../../../providers/conference-data';
+import { ModalController } from '@ionic/angular';
+import { PickSpeakersPage } from '../pick-speakers/pick-speakers.page';
+import { PickTracksPage } from '../pick-tracks/pick-tracks.page';
+
+@Component({
+ selector: 'session-edit',
+ templateUrl: './session-edit.page.html',
+ styleUrls: ['./session-edit.page.scss'],
+})
+export class SessionEditPage implements OnInit {
+
+ mode: string;
+ id: string;
+ session: Session;
+ minYear: string;
+ maxYear: string;
+ tracks: Track[];
+ speakers: Speaker[];
+ s_tracks: { name: string, isChecked: boolean }[] = [];
+ moreSpeakers = false;
+
+ constructor(private activatedRoute: ActivatedRoute,
+ private cdRef: ChangeDetectorRef,
+ private modalCtrl: ModalController,
+ private sessionProvider: SessionData,
+ private speakerProvider: SpeakerData,
+ private confProvider: ConferenceData,
+ private funProvider: FunctionlData,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.mode = this.activatedRoute.snapshot.paramMap.get('id');
+ if (this.mode === 'New') {
+ const today = new Date();
+ this.minYear = '' + (today.getFullYear() - 20);
+ this.maxYear = '' + (+this.minYear + 40);
+ this.session = {
+ name: '',
+ date: this.funProvider.getDateFormat(), // 2018-12-06
+ timeStart: '10:00', // 15:30 for 3:30pm
+ timeEnd: '10:00',
+ location: '',
+ description: '',
+ speakerIDs: [], // speaker's id
+ tracks: [], // name of track
+ };
+ } else {
+ [this.id, this.mode] = [this.mode, 'Edit'];
+ this.sessionProvider.getSession(this.id).then(data => {
+ this.session = data;
+ this.session.id = this.id;
+ this.minYear = '' + (+data.date.slice(0, 4) - 5);
+ this.maxYear = '' + (+this.minYear + 15);
+ });
+ }
+ this.getSpeakersTracks();
+ }
+
+ getSpeakersTracks() {
+ this.speakerProvider.getSpeakers().subscribe(data => {
+ this.speakers = data;
+ });
+
+ this.confProvider.getTracks().subscribe(data => {
+ this.tracks = data;
+ });
+ }
+
+ isInSession(key: string, type: string): any {
+ if (type === 'Speaker') {
+ return this.session.speakerIDs.find(id => id === key);
+ }
+ return this.session.tracks.find(name => name === key);
+ }
+
+ onRemoveSpeaker(s_id) {
+ const idx = this.session.speakerIDs.findIndex(id => id === s_id);
+ if (idx > -1) {
+ this.session.speakerIDs.splice(idx, 1);
+ }
+ }
+
+ async selectSpeakers() {
+ const modal = await this.modalCtrl.create({
+ component: PickSpeakersPage,
+ componentProps: { speakers: this.speakers, ids: this.session.speakerIDs }
+ });
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.session.speakerIDs = data;
+ }
+ }
+
+ onRemoveTrack(s_name) {
+ const idx = this.session.tracks.findIndex(name => name === s_name);
+ if (idx > -1) {
+ this.session.tracks.splice(idx, 1);
+ }
+ }
+
+ async selectTracks() {
+ const modal = await this.modalCtrl.create({
+ component: PickTracksPage,
+ componentProps: { tracks: this.tracks, names: this.session.tracks }
+ });
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.session.tracks = data;
+ }
+ }
+
+ changeTimeEnd(value) {
+ this.cdRef.detectChanges();
+ if (value > this.session.timeEnd) {
+ this.session.timeEnd = this.funProvider.addMinute(value);
+ }
+ }
+
+ confirmTimeEnd(value) {
+ this.cdRef.detectChanges();
+ if (value <= this.session.timeStart) {
+ this.funProvider.onError('Confirm Time', 'Wrong time for To. Try again.');
+ this.session.timeEnd = this.funProvider.addMinute(this.session.timeStart);
+ }
+ }
+
+ onSubmit() {
+ this.session.name = this.session.name.trim();
+ this.session.location = this.session.location.trim();
+ if (this.isValidAll()) {
+ if (this.mode === 'New') {
+ console.log('saved', this.session);
+ this.sessionProvider.addNewSession(this.session);
+ } else {
+ console.log('updated', this.session);
+ this.sessionProvider.updateSession(this.session);
+ }
+ this.onExit();
+ }
+ }
+
+ onExit() {
+ this.router.navigateByUrl('/setup/tabs/(sessions:sessions)');
+ }
+
+ isValidAll() {
+ if (this.session.name.length === 0) {
+ this.funProvider.onError('Confirm Title', 'Need a Title for the session. Try again.');
+ return false;
+ } else if (this.session.timeEnd <= this.session.timeStart) {
+ this.funProvider.onError('Confirm Time', 'TimeTo has to be later than TimeFrom. Try again.');
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/app/setup/sessions/sessions-setup.html b/src/app/setup/sessions/sessions-setup.html
new file mode 100644
index 000000000..7921311b0
--- /dev/null
+++ b/src/app/setup/sessions/sessions-setup.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Sessions
+
+ Add New
+
+
+
+
+
+
+
+
+
+
+ {{session.name}} ( {{ session.date }} )
+
+
+
+
+
+
+
+ Period
+
+ {{ endDate }}
+ {{ startDate }}
+ New
+
+
+
+
diff --git a/src/app/setup/sessions/sessions-setup.scss b/src/app/setup/sessions/sessions-setup.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/sessions/sessions-setup.ts b/src/app/setup/sessions/sessions-setup.ts
new file mode 100644
index 000000000..22a19079a
--- /dev/null
+++ b/src/app/setup/sessions/sessions-setup.ts
@@ -0,0 +1,98 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController, ModalController } from '@ionic/angular';
+import { Router } from '@angular/router';
+
+import { UserData } from '../../providers/user-data';
+import { SessionData } from '../../providers/session-data';
+
+import { Session } from '../../models';
+import { ConferenceData } from '../../providers/conference-data';
+import { FunctionlData } from '../../providers/function-data';
+import { PeriodPage } from '../../setup/period/period.page';
+
+@Component({
+ selector: 'sessions',
+ templateUrl: './sessions-setup.html',
+ styleUrls: ['./sessions-setup.scss'],
+})
+export class SessionsSetup implements OnInit {
+
+ startDate = '2000-01-01';
+ endDate = '2100-12-31';
+ queryText = '';
+ sessions: Session[];
+
+ constructor(private userProvider: UserData,
+ private sessionProvider: SessionData,
+ private dataProvider: ConferenceData,
+ private funProvider: FunctionlData,
+ private alertCtrl: AlertController,
+ private modalCtrl: ModalController,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.userProvider.getUser().then(user => {
+ if ( !user || user.username !== 'admin') {
+ this.router.navigateByUrl('/tutorial');
+ } else {
+ this.dataProvider.getPeriod().then(period => {
+ if (period) {
+ this.startDate = period.start;
+ this.endDate = period.end;
+ this.getSessions();
+ } else {
+ this.getDatePeriod();
+ }
+ });
+ }
+ });
+ }
+
+ getSessions() {
+ this.sessionProvider.getSessionsInPeriod(this.startDate, this.endDate)
+ .subscribe(data => { this.sessions = data; });
+ }
+
+ async getDatePeriod() {
+ const start = this.startDate ? this.startDate : this.funProvider.getDateFormat();
+ const end = this.endDate ? this.endDate : start ;
+ const modal = await this.modalCtrl.create({
+ component: PeriodPage,
+ componentProps: { start, end }
+ });
+ await modal.present();
+
+ const { data } = await modal.onWillDismiss();
+ if (data) {
+ this.startDate = data.start;
+ this.endDate = data.end;
+ this.dataProvider.setPeriod({ start: this.startDate, end: this.endDate });
+ this.getSessions();
+ }
+ }
+
+ async onConfirmToRemove(session: Session) {
+ const alert = await this.alertCtrl.create({
+ header: 'Confirm Remove',
+ message: `Are you sure to remove ${session.name}?`,
+ buttons: [
+ {
+ text: 'Cancel',
+ role: 'cancel',
+ handler: () => {
+ this.router.navigateByUrl('setup/tabs/(sessions:sessions)');
+ }
+ },
+ {
+ text: 'Remove',
+ handler: () => {
+ this.sessionProvider.removeSession(session);
+ this.router.navigateByUrl('setup/tabs/(sessions:sessions)');
+ }
+ }
+ ],
+ backdropDismiss: false
+ });
+ await alert.present();
+ }
+}
diff --git a/src/app/setup/sessions/sessions.module.ts b/src/app/setup/sessions/sessions.module.ts
new file mode 100644
index 000000000..1d3fcdeeb
--- /dev/null
+++ b/src/app/setup/sessions/sessions.module.ts
@@ -0,0 +1,39 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { SessionsSetup } from './sessions-setup';
+import { PickSpeakersPage } from './pick-speakers/pick-speakers.page';
+import { PickTracksPage } from './pick-tracks/pick-tracks.page';
+import { PeriodPage } from '../../setup/period/period.page';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SessionsSetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [
+ SessionsSetup,
+ PickSpeakersPage,
+ PickTracksPage,
+ PeriodPage
+ ],
+ entryComponents: [
+ PickSpeakersPage,
+ PickTracksPage,
+ PeriodPage
+ ]
+})
+export class SessionsSetupModule {}
diff --git a/src/app/setup/speakers/speaker-edit/speaker-edit.module.ts b/src/app/setup/speakers/speaker-edit/speaker-edit.module.ts
new file mode 100644
index 000000000..5343975ff
--- /dev/null
+++ b/src/app/setup/speakers/speaker-edit/speaker-edit.module.ts
@@ -0,0 +1,33 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Routes, RouterModule } from '@angular/router';
+import { IonicModule } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+
+import { SpeakerEditPage } from './speaker-edit.page';
+import { UploadImgComponent } from '../../upload-image/upload-img.page';
+import { DropzoneDirective } from '../../../directive/dropzone.directive';
+import { FilesizePipe } from '../../../pipe/filesize.pipe';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SpeakerEditPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [
+ SpeakerEditPage,
+ DropzoneDirective,
+ FilesizePipe,
+ UploadImgComponent
+ ]
+})
+export class SpeakerEditPageModule {}
diff --git a/src/app/setup/speakers/speaker-edit/speaker-edit.page.html b/src/app/setup/speakers/speaker-edit/speaker-edit.page.html
new file mode 100644
index 000000000..7a2f81224
--- /dev/null
+++ b/src/app/setup/speakers/speaker-edit/speaker-edit.page.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ {{mode}} Speaker
+
+ Cancel
+ Save
+
+
+
+
+
+
+
+
+
+
+
+ Name :
+
+
+
+ Email :
+
+
+
+ Phone :
+
+
+
+ Location :
+
+
+
+ twitter :
+
+
+
+ github :
+
+
+
+ instagram :
+
+
+
+ About :
+
+
+
+
+
+
+
+
diff --git a/src/app/setup/speakers/speaker-edit/speaker-edit.page.scss b/src/app/setup/speakers/speaker-edit/speaker-edit.page.scss
new file mode 100644
index 000000000..ceb2a9c10
--- /dev/null
+++ b/src/app/setup/speakers/speaker-edit/speaker-edit.page.scss
@@ -0,0 +1,4 @@
+img {
+ max-width: 140px;
+ border-radius: 50%;
+}
\ No newline at end of file
diff --git a/src/app/setup/speakers/speaker-edit/speaker-edit.page.ts b/src/app/setup/speakers/speaker-edit/speaker-edit.page.ts
new file mode 100644
index 000000000..77fa9f988
--- /dev/null
+++ b/src/app/setup/speakers/speaker-edit/speaker-edit.page.ts
@@ -0,0 +1,116 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { Speaker } from '../../../models';
+import { SpeakerData } from '../../../providers/speaker-data';
+import { FunctionlData } from '../../../providers/function-data';
+
+@Component({
+ selector: 'speaker-edit',
+ templateUrl: './speaker-edit.page.html',
+ styleUrls: ['./speaker-edit.page.scss'],
+})
+export class SpeakerEditPage implements OnInit {
+
+ header = `Speaker's Info`;
+ mode: string;
+ id: string;
+ speaker: Speaker;
+ loadImage = false;
+ currentUrl = '';
+
+ constructor(private activatedRoute: ActivatedRoute,
+ private speakerProvider: SpeakerData,
+ private funProvider: FunctionlData,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.mode = this.activatedRoute.snapshot.paramMap.get('id');
+ if (this.mode === 'New') {
+ this.speaker = {
+ name: '',
+ profilePic: '',
+ twitter: '',
+ github: '',
+ instagram: '',
+ about: '',
+ location: '',
+ email: '',
+ phone: '',
+ sessions: [],
+ };
+ } else {
+ [this.id, this.mode] = [this.mode, 'Edit'];
+ this.speakerProvider.getSpeaker(this.id).then(data => {
+ this.speaker = data;
+ this.speaker.id = this.id;
+ // to keep profile's path as string.
+ this.currentUrl = data.profilePic;
+ });
+ }
+ }
+
+ onSubmit() {
+ if (this.verifiedInput()) {
+ if (this.mode === 'New') {
+ this.speakerProvider.addNewSpeaker(this.speaker);
+ } else {
+ // to reassign it's origin path as string.
+ this.speaker.profilePic = this.currentUrl;
+ this.speakerProvider.updateSpeaker(this.speaker);
+ }
+ this.router.navigateByUrl('/setup/tabs/(speaker:speaker)');
+ }
+ }
+
+ verifiedInput() {
+ this.speaker.name = this.speaker.name.trim() ;
+ this.speaker.profilePic = this.speaker.profilePic.trim() ;
+ this.speaker.email = this.speaker.email.trim() ;
+ this.speaker.phone = this.speaker.phone.trim() ;
+ this.speaker.github = this.speaker.github.trim();
+ this.speaker.instagram = this.speaker.instagram.trim() ;
+ this.speaker.location = this.speaker.location.trim() ;
+ this.speaker.twitter = this.speaker.twitter.trim() ;
+ this.speaker.about = this.speaker.about.trim() ;
+ if (this.speaker.name.length < 4) {
+ this.funProvider.onError(this.header, 'Name should be more than 3 digits.');
+ } if (this.isNameUsed()) {
+ this.funProvider.onError(this.header, 'Name already exist. Try another.');
+ } if (this.isEmailUsed()) {
+ this.funProvider.onError(this.header, 'Email is not valid. Try another.');
+ } if (this.speaker.phone.length < 10) {
+ this.funProvider.onError(this.header, 'Phone number is not valid. Try again.');
+ } else {
+ return true;
+ }
+ return false;
+ }
+
+ isNameUsed() {
+ // check validation of name
+ return false;
+ }
+
+ isEmailUsed() {
+ // check validation of email
+ return false;
+ }
+
+ onExit() {
+ this.router.navigateByUrl('/setup/tabs/(speaker:speaker)');
+ }
+
+ updatePicture(path: string) {
+ const oldUrl = this.speaker.profilePic;
+ const id = this.speaker.id;
+ this.speaker.profilePic = path;
+ this.speakerProvider.updateSpeaker(this.speaker);
+ this.loadImage = false;
+ this.router.navigateByUrl('/setup/tabs/(speaker:speaker)');
+ }
+
+ onLoadImage() {
+ this.loadImage = (this.mode === 'New') ? false : true;
+ }
+}
diff --git a/src/app/setup/speakers/speakers-setup.html b/src/app/setup/speakers/speakers-setup.html
new file mode 100644
index 000000000..3641bedd3
--- /dev/null
+++ b/src/app/setup/speakers/speakers-setup.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Speakers
+
+ Add New
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{speaker.name}} ({{ speaker.phone }})
+
+
+
+
+
+
diff --git a/src/app/setup/speakers/speakers-setup.scss b/src/app/setup/speakers/speakers-setup.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/speakers/speakers-setup.ts b/src/app/setup/speakers/speakers-setup.ts
new file mode 100644
index 000000000..f25c2b354
--- /dev/null
+++ b/src/app/setup/speakers/speakers-setup.ts
@@ -0,0 +1,59 @@
+import { Component, OnInit } from '@angular/core';
+import { SpeakerData } from '../../providers/speaker-data';
+import { Speaker } from '../../models';
+import { AlertController } from '@ionic/angular';
+import { Router } from '@angular/router';
+import { UserData } from '../../providers/user-data';
+
+@Component({
+ selector: 'speakers-setup',
+ templateUrl: './speakers-setup.html',
+ styleUrls: ['./speakers-setup.scss'],
+})
+export class SpeakersSetup implements OnInit {
+
+ speakers: Speaker[];
+ queryText = '';
+
+ constructor(private speakerProvider: SpeakerData,
+ private userProvider: UserData,
+ private alertCtrl: AlertController,
+ private router: Router) { }
+
+ ngOnInit() {
+ this.userProvider.getUser().then(user => {
+ if ( !user || user.username !== 'admin') {
+ this.router.navigateByUrl('/tutorial');
+ } else {
+ this.speakerProvider.getSpeakers().subscribe(data => {
+ this.speakers = data;
+ });
+ }
+ });
+ }
+
+ async onConfirmToRemove(speaker: Speaker) {
+ const alert = await this.alertCtrl.create({
+ header: 'Confirm Remove',
+ message: `Are you sure to remove ${speaker.name}?`,
+ buttons: [
+ {
+ text: 'Cancel',
+ role: 'cancel',
+ handler: () => {
+ this.router.navigateByUrl('setup/tabs/(speaker:speaker)');
+ }
+ },
+ {
+ text: 'Remove',
+ handler: () => {
+ this.speakerProvider.removeSpeaker(speaker);
+ this.router.navigateByUrl('setup/tabs/(speaker:speaker)');
+ }
+ }
+ ],
+ backdropDismiss: false
+ });
+ await alert.present();
+ }
+}
diff --git a/src/app/setup/speakers/speakers.module.ts b/src/app/setup/speakers/speakers.module.ts
new file mode 100644
index 000000000..6e299c91f
--- /dev/null
+++ b/src/app/setup/speakers/speakers.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { SpeakersSetup } from './speakers-setup';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SpeakersSetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [SpeakersSetup]
+})
+export class SpeakersSetupModule {}
diff --git a/src/app/setup/support/support-setup.html b/src/app/setup/support/support-setup.html
new file mode 100644
index 000000000..6250660cc
--- /dev/null
+++ b/src/app/setup/support/support-setup.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Support
+
+
+
+
+
+
diff --git a/src/app/setup/support/support-setup.scss b/src/app/setup/support/support-setup.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/support/support-setup.ts b/src/app/setup/support/support-setup.ts
new file mode 100644
index 000000000..9dd6c8f81
--- /dev/null
+++ b/src/app/setup/support/support-setup.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'support',
+ templateUrl: './support-setup.html',
+ styleUrls: ['./support-setup.scss'],
+})
+export class SupportSetup implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/app/setup/support/support.module.ts b/src/app/setup/support/support.module.ts
new file mode 100644
index 000000000..860304a42
--- /dev/null
+++ b/src/app/setup/support/support.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { SupportSetup } from './support-setup';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: SupportSetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [SupportSetup]
+})
+export class SupportSetupModule {}
diff --git a/src/app/setup/tabs-setup/tabs-setup-routing.module.ts b/src/app/setup/tabs-setup/tabs-setup-routing.module.ts
new file mode 100644
index 000000000..66ef3588f
--- /dev/null
+++ b/src/app/setup/tabs-setup/tabs-setup-routing.module.ts
@@ -0,0 +1,42 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { TabsSetupPage } from './tabs-setup.page';
+import { SessionsSetup } from '../sessions/sessions-setup';
+import { SpeakersSetup } from '../speakers/speakers-setup';
+import { TracksSetup } from '../tracks/tracks-setup';
+import { MapSetup } from '../map/map-setup';
+import { SupportSetup } from '../support/support-setup';
+import { PartOfDaySetup } from '../part-of-day/part-of-day-setup';
+import { PartOfDayNewPage } from '../part-of-day/part-of-day-new/part-of-day-new.page';
+import { SpeakerEditPage } from '../speakers/speaker-edit/speaker-edit.page';
+import { SessionEditPage } from '../sessions/session-edit/session-edit.page';
+
+const routes: Routes = [
+ {
+ path: 'tabs', component: TabsSetupPage,
+ children: [
+ // tab one
+ { path: 'sessions', component: SessionsSetup, outlet: 'sessions' },
+ { path: 'update/:id', component: SessionEditPage, outlet: 'sessions'},
+ // tab two
+ { path: 'speaker', component: SpeakersSetup, outlet: 'speaker' },
+ { path: 'edit/:id', component: SpeakerEditPage, outlet: 'speaker' },
+ // tab three
+ { path: 'tracks', component: TracksSetup, outlet: 'tracks' },
+ // tab four
+ { path: 'partofday', component: PartOfDaySetup, outlet: 'partofday' },
+ { path: 'new', component: PartOfDayNewPage, outlet: 'partofday' },
+ // tab five
+ { path: 'map', component: MapSetup, outlet: 'map' },
+ // tab six
+ { path: 'support', component: SupportSetup, outlet: 'support' },
+ ]
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class TabsSetupRoutingModule { }
diff --git a/src/app/setup/tabs-setup/tabs-setup.module.ts b/src/app/setup/tabs-setup/tabs-setup.module.ts
new file mode 100644
index 000000000..35af3fa3b
--- /dev/null
+++ b/src/app/setup/tabs-setup/tabs-setup.module.ts
@@ -0,0 +1,47 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { TabsSetupPage } from './tabs-setup.page';
+import { TabsSetupRoutingModule } from './tabs-setup-routing.module';
+import { SessionsSetupModule } from '../sessions/sessions.module';
+import { SpeakersSetupModule } from '../speakers/speakers.module';
+import { TracksSetupModule } from '../tracks/tracks.module';
+import { MapSetupModule } from '../map/map.module';
+import { SupportSetupModule } from '../support/support.module';
+import { PartOfDaySetupModule } from '../part-of-day/part-of-day.module';
+import { PartOfDayNewPageModule } from '../part-of-day/part-of-day-new/part-of-day-new.module';
+import { SpeakerEditPageModule } from '../speakers/speaker-edit/speaker-edit.module';
+import { SessionEditPageModule } from '../sessions/session-edit/session-edit.module';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: TabsSetupPage
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ SessionsSetupModule,
+ SessionEditPageModule,
+ SpeakersSetupModule,
+ SpeakerEditPageModule,
+ TracksSetupModule,
+ PartOfDaySetupModule,
+ PartOfDayNewPageModule,
+ MapSetupModule,
+ SupportSetupModule,
+ TabsSetupRoutingModule
+ ],
+ declarations: [
+ TabsSetupPage
+ ]
+})
+export class TabsSetupPageModule {}
diff --git a/src/app/setup/tabs-setup/tabs-setup.page.html b/src/app/setup/tabs-setup/tabs-setup.page.html
new file mode 100644
index 000000000..35cc8feaf
--- /dev/null
+++ b/src/app/setup/tabs-setup/tabs-setup.page.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Sessions
+
+
+
+
+ Speakers
+
+
+
+
+ Tracks
+
+
+
+
+ PartOfDay
+
+
+
+
+ Map
+
+
+
+
+ Support
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/setup/tabs-setup/tabs-setup.page.scss b/src/app/setup/tabs-setup/tabs-setup.page.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/tabs-setup/tabs-setup.page.ts b/src/app/setup/tabs-setup/tabs-setup.page.ts
new file mode 100644
index 000000000..5464c1731
--- /dev/null
+++ b/src/app/setup/tabs-setup/tabs-setup.page.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'tabs-setup',
+ templateUrl: './tabs-setup.page.html',
+ styleUrls: ['./tabs-setup.page.scss'],
+})
+export class TabsSetupPage implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/src/app/setup/tracks/tracks-setup.html b/src/app/setup/tracks/tracks-setup.html
new file mode 100644
index 000000000..59d4b0e26
--- /dev/null
+++ b/src/app/setup/tracks/tracks-setup.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Tracks
+
+ Add New
+
+
+
+
+
+
+
+
+ {{track.name}}
+
+ Edit
+
+
+
+
+
diff --git a/src/app/setup/tracks/tracks-setup.scss b/src/app/setup/tracks/tracks-setup.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/setup/tracks/tracks-setup.ts b/src/app/setup/tracks/tracks-setup.ts
new file mode 100644
index 000000000..de8b8f1ec
--- /dev/null
+++ b/src/app/setup/tracks/tracks-setup.ts
@@ -0,0 +1,136 @@
+import { Component, OnInit } from '@angular/core';
+import { UserData } from '../../providers/user-data';
+import { Router } from '@angular/router';
+import { Track, Session } from '../../models';
+import { SessionData } from '../../providers/session-data';
+import { ConferenceData } from '../../providers/conference-data';
+import { AlertController } from '@ionic/angular';
+import { FunctionlData } from '../../providers/function-data';
+
+@Component({
+ selector: 'tracks',
+ templateUrl: './tracks-setup.html',
+ styleUrls: ['./tracks-setup.scss'],
+})
+export class TracksSetup implements OnInit {
+
+ header = `Track's Info`;
+ tracks: Track[];
+ sessions: Session[];
+ newName: string;
+ succeed = false;
+ jobDescription: string;
+
+ constructor(private userProvider: UserData,
+ private sessionProvider: SessionData,
+ private confProvider: ConferenceData,
+ private funProvider: FunctionlData,
+ private router: Router,
+ private alertCtrl: AlertController) { }
+
+ ngOnInit() {
+ this.userProvider.getUser().then(user => {
+ if ( !user || user.username !== 'admin') {
+ this.router.navigateByUrl('/tutorial');
+ } else {
+ this.confProvider.getTracks().subscribe(data => {
+ this.tracks = data;
+ this.sessionProvider.getSessions().subscribe(session => {
+ this.sessions = session;
+ });
+ });
+ }
+ });
+ }
+
+ async addNewTrack() {
+ const addForm = await this.alertCtrl.create({
+ header: 'Add a Track',
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ data.newName = data.newName.trim();
+ if (this.isTheValueUsed(data.newName)) {
+ this.funProvider.onError(this.header, data.newName + ' was used already. Try another.');
+ } else {
+ this.confProvider.addTrack({ name: data.newName });
+ this.succeed = true;
+ this.jobDescription = 'A new Track has been added.';
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'text',
+ name: 'newName',
+ placeholder: 'new name here'
+ }
+ ],
+ backdropDismiss: false
+ });
+ await addForm.present();
+ }
+
+ async changeName(track) {
+ this.succeed = false;
+ const changeForm = await this.alertCtrl.create({
+ header: 'Change Track',
+ subHeader: track.name,
+ buttons: [
+ 'Cancel',
+ {
+ text: 'Ok',
+ handler: (data: any) => {
+ data.newName = data.newName.trim();
+ if (this.isTheValueUsed(data.newName)) {
+ this.funProvider.onError(this.header, data.newName + ' was used already. Try another.');
+ } else {
+ this.confProvider.updateTrack(track, data.newName );
+ this.succeed = true;
+ this.jobDescription = 'Name has been changed.';
+ }
+ }
+ }
+ ],
+ inputs: [
+ {
+ type: 'text',
+ name: 'newName',
+ placeholder: 'new name here'
+ }
+ ],
+ backdropDismiss: false
+ });
+ await changeForm.present();
+ }
+
+ async onConfirmToRemove(track: Track) {
+ const alert = await this.alertCtrl.create({
+ header: 'Confirm Remove',
+ message: `Are you sure to remove ${track.name}?`,
+ buttons: [
+ {
+ text: 'Cancel',
+ role: 'cancel',
+ handler: () => {
+ }
+ },
+ {
+ text: 'Remove',
+ handler: () => {
+ this.confProvider.removeTrack(track);
+ }
+ }
+ ],
+ backdropDismiss: false
+ });
+ await alert.present();
+ }
+
+ isTheValueUsed(name) {
+ return this.tracks.find(track => track.name.toLowerCase() === name.toLowerCase());
+ }
+}
diff --git a/src/app/setup/tracks/tracks.module.ts b/src/app/setup/tracks/tracks.module.ts
new file mode 100644
index 000000000..c014c8a37
--- /dev/null
+++ b/src/app/setup/tracks/tracks.module.ts
@@ -0,0 +1,26 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { IonicModule } from '@ionic/angular';
+
+import { TracksSetup } from './tracks-setup';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: TracksSetup
+ }
+];
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ IonicModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [TracksSetup]
+})
+export class TracksSetupModule {}
diff --git a/src/app/setup/upload-image/upload-img.page.html b/src/app/setup/upload-image/upload-img.page.html
new file mode 100644
index 000000000..ee5ff5519
--- /dev/null
+++ b/src/app/setup/upload-image/upload-img.page.html
@@ -0,0 +1,26 @@
+Upload Speaker's Avatar
+
+
+
Image Drop Zone
+
Drag and Drop a File
+
+
+
+
+
+Cancel
+
+
+
+ {{ pct | number }}%
+
+
+
+ {{ snap.bytesTransferred | filesize }} of {{ snap.totalBytes | filesize }}
+
diff --git a/src/app/setup/upload-image/upload-img.page.scss b/src/app/setup/upload-image/upload-img.page.scss
new file mode 100644
index 000000000..3c4bf3bd1
--- /dev/null
+++ b/src/app/setup/upload-image/upload-img.page.scss
@@ -0,0 +1,43 @@
+.title {
+ font-size: 1.4em;
+ margin-top: 40px;
+}
+
+.dropzone {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ font-weight: 200;
+ height: 300px;
+ border: 2px dashed #f16624;
+ border-radius: 5px;
+ background: white;
+ margin: 40px 0;
+
+ &.hovering {
+ border: 2px solid #f16624;
+ color: #dadada !important;
+ }
+
+ .file-label {
+ font-size: 1em;
+ }
+}
+
+progress::-webkit-progress-value {
+ transition: width 0.1s ease;
+}
+
+img {
+ width: 250px
+}
+
+button {
+ padding: 10px;
+ width: 150px;
+ font-size: 1em;
+ font-weight: 400;
+ background-color: #f16624;
+ color: white
+}
diff --git a/src/app/setup/upload-image/upload-img.page.ts b/src/app/setup/upload-image/upload-img.page.ts
new file mode 100644
index 000000000..ed87ae3df
--- /dev/null
+++ b/src/app/setup/upload-image/upload-img.page.ts
@@ -0,0 +1,72 @@
+import { Component, Output, Input, EventEmitter } from '@angular/core';
+import { AngularFireStorage, AngularFireUploadTask } from 'angularfire2/storage';
+import { AngularFirestore } from 'angularfire2/firestore';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+
+@Component({
+ selector: 'upload-img',
+ templateUrl: './upload-img.page.html',
+ styleUrls: ['./upload-img.page.scss']
+})
+export class UploadImgComponent {
+
+ @Input() name: string;
+ @Output() exit = new EventEmitter();
+ @Output() updateImage = new EventEmitter();
+
+ // Main task
+ task: AngularFireUploadTask;
+
+ // State for dropzone CSS toggling
+ isHovering: boolean;
+
+ // Progress monitoring
+ percentage: Observable;
+
+ snapshot: Observable;
+
+ // constructor(private db: AngularFirestore) { }
+ constructor(private storage: AngularFireStorage, private db: AngularFirestore) { }
+
+ toggleHover(event: boolean) {
+ this.isHovering = event;
+ }
+
+ startUpload(event: FileList) {
+ // The File object
+ const file = event.item(0);
+
+ // Client-side validation example
+ if (!file || file.type.split('/')[0] !== 'image') {
+ console.error('unsupported file type :( ');
+ return;
+ }
+
+ // The storage path
+ const path = `images/${new Date().getTime()}_${file.name}`;
+
+ // Totally optional metadata
+ const customMetadata = { app: 'My AngularFire-powered PWA!' };
+
+ // const ref = this.storage.ref(path);
+ // this.task = ref.put(file, { customMetadata });
+ // The main task
+ this.task = this.storage.upload(path, file, { customMetadata });
+
+ // Progress monitoring
+ this.percentage = this.task.percentageChanges();
+ this.snapshot = this.task.snapshotChanges().pipe(
+ tap(snap => {
+ if (snap.bytesTransferred === snap.totalBytes) {
+ this.updateImage.emit(path);
+ }
+ })
+ );
+ }
+
+ // Determines if the upload task is active
+ isActive(snapshot) {
+ return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes;
+ }
+}
diff --git a/src/assets/data/data.json b/src/assets/data/data.json
index 717bc0a35..fcbe9776c 100755
--- a/src/assets/data/data.json
+++ b/src/assets/data/data.json
@@ -3,7 +3,7 @@
"schedule": [{
"date": "2047-05-17",
"groups": [{
- "time": "8:00 am",
+ "time": "Morning",
"sessions": [{
"name": "Breakfast",
"timeStart": "8:00 am",
@@ -11,10 +11,7 @@
"location": "Dining Hall",
"tracks": ["Food"],
"id": "1"
- }]
- }, {
- "time": "9:15 am",
- "sessions": [{
+ }, {
"name": "Getting Started with Ionic",
"location": "Hall 2",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
@@ -22,6 +19,7 @@
"timeStart": "9:30 am",
"timeEnd": "9:45 am",
"tracks": ["Ionic"],
+ "hide": true,
"id": "2"
}, {
"name": "Ionic Tooling",
@@ -41,10 +39,7 @@
"timeEnd": "9:30 am",
"tracks": ["Ionic"],
"id": "4"
- }]
- }, {
- "time": "10:00 am",
- "sessions": [{
+ }, {
"name": "Migrating to Ionic",
"location": "Hall 1",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
@@ -80,10 +75,7 @@
"timeEnd": "11:00 am",
"tracks": ["Services"],
"id": "8"
- }]
- }, {
- "time": "11:00 am",
- "sessions": [{
+ }, {
"name": "Ionic Workshop",
"location": "Hall 1",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
@@ -112,7 +104,7 @@
"id": "11"
}]
}, {
- "time": "12:00 pm",
+ "time": "Afternoon",
"sessions": [{
"name": "Lunch",
"location": "Dining Hall",
@@ -121,10 +113,7 @@
"timeEnd": "1:00 pm",
"tracks": ["Food"],
"id": "12"
- }]
- }, {
- "time": "1:00 pm",
- "sessions": [{
+ }, {
"name": "Ionic in the Enterprise",
"location": "Hall 1",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
@@ -151,10 +140,7 @@
"timeEnd": "2:00 pm",
"tracks": ["Services"],
"id": "15"
- }]
- }, {
- "time": "2:00 pm",
- "sessions": [{
+ }, {
"name": "Push Notifications in Ionic",
"location": "Hall 2",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
@@ -181,10 +167,7 @@
"timeEnd": "3:00 pm",
"tracks": ["Design"],
"id": "18"
- }]
- }, {
- "time": "3:00",
- "sessions": [{
+ }, {
"name": "Angular Directives in Ionic",
"location": "Hall 1",
"description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with Angular, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and Angular.",
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
deleted file mode 100644
index 3612073bc..000000000
--- a/src/environments/environment.prod.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const environment = {
- production: true
-};
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
deleted file mode 100644
index ad4196020..000000000
--- a/src/environments/environment.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// The file contents for the current environment will overwrite these during build.
-// The build system defaults to the dev environment which uses `environment.ts`, but if you do
-// `ng build --env=prod` then `environment.prod.ts` will be used instead.
-// The list of which env maps to which file can be found in `.angular-cli.json`.
-export const environment = {
- production: false
-};
-
-/*
- * In development mode, to ignore zone related error stack frames such as
- * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
- * import the following file, but please comment it out in production mode
- * because it will have performance impact when throw error
- */
-// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/src/index.html b/src/index.html
index b6e26a29c..46281cc6b 100644
--- a/src/index.html
+++ b/src/index.html
@@ -29,7 +29,7 @@
-
+
Please enable JavaScript to continue using this application.