Skip to content
This repository has been archived by the owner on Dec 12, 2020. It is now read-only.

Commit

Permalink
Add admin module
Browse files Browse the repository at this point in the history
  • Loading branch information
henry-thompson committed Dec 10, 2017
1 parent 0afd0c3 commit 85b7d20
Show file tree
Hide file tree
Showing 15 changed files with 396 additions and 3 deletions.
9 changes: 9 additions & 0 deletions database.rules.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"rules": {
"sponsors": {
".write": "auth != null",
"$guid": {
".read": true,
"competitions": {
Expand All @@ -22,6 +23,14 @@
".write": true
}
}
},
"admin": {
".read": "auth != null",
".write": "auth != null"
},
"benefits": {
".read": "auth != null",
".write": "auth != null"
}
}
}
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@angular/platform-browser": "^4.0.0",
"@angular/platform-browser-dynamic": "^4.0.0",
"@angular/router": "^4.0.0",
"angular2-uuid": "^1.1.1",
"angularfire2": "5.0.0-rc.3",
"core-js": "^2.4.1",
"firebase": "^4.8.0",
Expand Down
31 changes: 31 additions & 0 deletions src/app/admin/admin.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<section class="red">
<div class="container width">
<div class="one unit row">
<h1>Sponsorship Portal Admin</h1>
</div>
<div class="one unit row">
<h3>Manage sponsors' accounts.</h3>
</div>
</div>
</section>

<section>
<div class="container width">
<div class="one unit row">
<h2>Sponsors</h2>
</div>
<div class="one unit row">
<h3>These sponsors are registered:</h3>
</div>
<div *ngFor="let sponsor of sponsors" class="half unit row">
<p>
<a rel="next" [routerLink]="[ '/', sponsor.id ]">{{ sponsor.name }}</a>
</p>
</div>
<div class="half unit row">
<p>
<a [routerLink]="[ '/admin/new' ]">Add Sponsor</a>
</p>
</div>
</div>
</section>
17 changes: 17 additions & 0 deletions src/app/admin/admin.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SponsorIndexModel } from 'app/admin/sponsor-index.model';
import { AdminService } from 'app/admin/admin.service';
import { Component } from '@angular/core';

@Component({
selector: 'app-admin',
templateUrl: './admin.component.html'
})
export class AdminComponent {
sponsors: SponsorIndexModel[];

constructor(private adminService: AdminService) {
adminService.getSponsors().subscribe(
sponsors => this.sponsors = (sponsors ? sponsors : [])
);
}
}
108 changes: 108 additions & 0 deletions src/app/admin/admin.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Injectable } from '@angular/core';
import { AngularFireObject, AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import { SponsorIndexModel } from 'app/admin/sponsor-index.model';
import { Observable } from 'rxjs/Observable';
import { SponsorshipBenefitModel } from 'app/benefits/sponsorship-benefit.model';
import { SponsorModel } from 'app/admin/sponsor.model';
import { UUID } from 'angular2-uuid';
import { SponsorsService } from 'app/sponsors/sponsors.service';
import { FirebaseApp } from 'angularfire2';
import { AngularFireAuth } from 'angularfire2/auth';

@Injectable()
export class AdminService {
constructor(private auth: AngularFireAuth,
private db: AngularFireDatabase) {}

loginUser(email: string, password: string): Promise<void> {
return this.auth.auth.signInWithEmailAndPassword(email, password);
}

isLoggedIn(): Observable<boolean> {
return this.auth.authState.map(
user => user != null
);
}

private getSponsorIndexObjects(): AngularFireObject<SponsorIndexModel[]> {
return this.db.object('admin/sponsors');
}

private getSponsorName(guid: string): AngularFireObject<string> {
return this.db.object(`sponsors/${guid}/name`);
}

private getBenefitsObjects(): AngularFireObject<SponsorshipBenefitModel[]> {
return this.db.object('benefits');
}

private getSponsorObjects(): AngularFireList<SponsorModel> {
return this.db.list('sponsors');
}

getSponsors(): Observable<SponsorIndexModel[]> {
return this.getSponsorIndexObjects().valueChanges();
}

getBenefits(): Observable<SponsorshipBenefitModel[]> {
return this.getBenefitsObjects().valueChanges();
}

saveBenefits(benefits: SponsorshipBenefitModel[]): Promise<void> {
return this.getBenefitsObjects().set(benefits);
}

addSponsor(sponsor: SponsorModel): Observable<string | void> {
if (!(sponsor.benefits && sponsor.maxRecruiters && sponsor.name && sponsor.tier)) {
return Observable.throw(new Error('Sponsor details not filled out'));
}

const guid$ = this.generateUniqueGuid().first();

return guid$.flatMap(
guid => {
const setSponsor = Observable.defer<void>(() => this.addSponsorObject(guid, sponsor));
const setIndex = Observable.defer(() => this.addSponsorIndex(guid, sponsor.name));
const setGuid = Observable.defer<string>(() => Observable.of(guid));

return Observable.concat(setSponsor, setIndex, setGuid);
}
);
}

private generateUniqueGuid(): Observable<string> {
// Mae sure the GUID isn't already taken.
const guid = UUID.UUID();

return this.getSponsorName(guid).valueChanges().first().flatMap(
name => {
if (name) {
return this.generateUniqueGuid();
}
else {
return Observable.of(guid);
}
}
);
}

private addSponsorObject(guid: string, sponsor: SponsorModel): Promise<void> {
return this.db.object(`sponsors/${guid}`).set(sponsor);
}

private addSponsorIndex(guid: string, name: string): Observable<void> {
const index: SponsorIndexModel = {
id: guid,
name: name
};

const indexObjects = this.getSponsorIndexObjects();

return indexObjects.valueChanges().first().flatMap(
(indices: SponsorIndexModel[]) => {
indices.push(index);
return indexObjects.set(indices);
}
);
}
}
4 changes: 4 additions & 0 deletions src/app/admin/sponsor-index.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class SponsorIndexModel {
id: string;
name: string;
}
9 changes: 9 additions & 0 deletions src/app/admin/sponsor.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SponsorshipTier } from 'app/sponsors/sponsorship-tier.enum';
import { SponsorshipBenefitModel } from 'app/benefits/sponsorship-benefit.model';

export class SponsorModel {
name: string;
tier: SponsorshipTier;
maxRecruiters: number;
benefits: SponsorshipBenefitModel[];
}
19 changes: 17 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,17 @@ import { WorkshopService } from 'app/workshops/workshop.service';
import { TipsComponent } from 'app/tips/tips.component';
import { SponsorsService } from 'app/sponsors/sponsors.service';
import { TechService } from 'app/tech/tech.service';
import { AdminComponent } from 'app/admin/admin.component';
import { AdminService } from 'app/admin/admin.service';
import { NewSponsorComponent } from 'app/newSponsor/new-sponsor.component';
import { LoginComponent } from 'app/login/login.component';
import { AngularFireAuth } from 'angularfire2/auth';
import { AuthGuard } from 'app/auth-guard';

const appRoutes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
{ path: 'admin/new', component: NewSponsorComponent, canActivate: [AuthGuard] },
{ path: ':guid', component: PortalComponent },
{ path: ':guid/people', component: PeopleComponent },
{ path: ':guid/social-media', component: SocialMediaComponent },
Expand All @@ -52,7 +61,10 @@ const appRoutes: Routes = [
EventsComponent,
PresentationComponent,
ChangesSavedComponent,
TipsComponent
TipsComponent,
AdminComponent,
NewSponsorComponent,
LoginComponent
],
imports: [
BrowserModule,
Expand All @@ -72,7 +84,10 @@ const appRoutes: Routes = [
WorkshopService,
SponsorsService,
TechService,
AngularFireDatabase
AdminService,
AngularFireDatabase,
AngularFireAuth,
AuthGuard
],
bootstrap: [
AppComponent
Expand Down
22 changes: 22 additions & 0 deletions src/app/auth-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { CanActivate, CanActivateChild, Router } from '@angular/router';
import { AdminService } from 'app/admin/admin.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router,
private adminService: AdminService) { }

canActivate(): Observable<boolean> {
return this.adminService.isLoggedIn().map(
loggedIn => {
if (!loggedIn) {
this.router.navigate(['/login']);
}

return loggedIn;
}
);
}
}
2 changes: 1 addition & 1 deletion src/app/benefits/sponsorship-benefit.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SponsorBenefitTypes } from 'app/benefits/benefits.service';

export class SponsorshipBenefitModel {
id: SponsorBenefitTypes;
id: string;
name: string;
}
24 changes: 24 additions & 0 deletions src/app/login/login.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<section class="red">
<div class="container width">
<div class="one unit row">
<h1>Login</h1>
</div>
<div class="one unit row">
<h3>Hack Cambridge Sponsorship Admin.</h3>
</div>
</div>
</section>

<section>
<div class="container width">
<div class="one half unit row">
<div class="twelve unit column">
<fieldset>
<input type="text" placeholder="Email" [(ngModel)]="email">
<input type="password" placeholder="Password" [(ngModel)]="password">
<input type="button" value="Login" (click)="login()">
</fieldset>
</div>
</div>
</div>
</section>
30 changes: 30 additions & 0 deletions src/app/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { AdminService } from 'app/admin/admin.service';
import { Router } from '@angular/router';
import { Component } from '@angular/core';
import { OnInit } from '@angular/core/src/metadata/lifecycle_hooks';

@Component({
selector: 'app-login',
templateUrl: './login.component.html'
})
export class LoginComponent implements OnInit {
email: string;
password: string;

constructor(private adminService: AdminService,
private router: Router) {}

ngOnInit(): void {
if (this.adminService.isLoggedIn()) {
this.router.navigate([ '/admin' ]);
}
}

login(): void {
this.adminService.loginUser(this.email, this.password)
.then(success => this.router.navigate([ '/admin' ]))
.catch((error: Error) => {
alert(error.message);
});
}
}
Loading

0 comments on commit 85b7d20

Please sign in to comment.