diff options
Diffstat (limited to 'ufund-ui/src')
-rw-r--r-- | ufund-ui/src/app/app.component.css | 47 | ||||
-rw-r--r-- | ufund-ui/src/app/app.component.html | 12 | ||||
-rw-r--r-- | ufund-ui/src/app/app.component.ts | 42 | ||||
-rw-r--r-- | ufund-ui/src/app/app.module.ts | 2 | ||||
-rw-r--r-- | ufund-ui/src/app/components/dashboard/dashboard.component.ts | 6 | ||||
-rw-r--r-- | ufund-ui/src/app/components/funding-basket/funding-basket.component.ts | 21 | ||||
-rw-r--r-- | ufund-ui/src/app/components/login/login.component.html | 1 | ||||
-rw-r--r-- | ufund-ui/src/app/components/login/login.component.ts | 2 | ||||
-rw-r--r-- | ufund-ui/src/app/components/need-list/need-list.component.html | 2 | ||||
-rw-r--r-- | ufund-ui/src/app/components/toast/toast.component.css | 57 | ||||
-rw-r--r-- | ufund-ui/src/app/components/toast/toast.component.html | 7 | ||||
-rw-r--r-- | ufund-ui/src/app/components/toast/toast.component.ts | 37 | ||||
-rw-r--r-- | ufund-ui/src/app/services/auth.service.ts | 14 | ||||
-rw-r--r-- | ufund-ui/src/app/services/toasts.service.ts | 12 | ||||
-rw-r--r-- | ufund-ui/src/styles.css | 2 |
15 files changed, 195 insertions, 69 deletions
diff --git a/ufund-ui/src/app/app.component.css b/ufund-ui/src/app/app.component.css index 5af3958..0bcd658 100644 --- a/ufund-ui/src/app/app.component.css +++ b/ufund-ui/src/app/app.component.css @@ -38,36 +38,29 @@ text-decoration: none; } - a:hover { - /*color: light-dark(black, white)*/ - text-decoration: underline; + a { + display: block; + position: relative; + padding: 0.1em 0; } -} -.toast { - transform: translateY(-90px); - transition: transform .5s; - align-self: center; - z-index: 3; - position: absolute; - top: 15px; - display: flex; - flex-direction: row; - padding: 3px 15px; - background-color: #3a3a3a; - border-radius: 100000px; - gap: 10px; - align-items: center; + a::after { + content: ''; + position: absolute; + bottom: 4px; + left: 0; + width: 100%; + height: 0.03em; + background-color: white; + opacity: 0; + transition: opacity 300ms, transform 300ms; + } - button { - aspect-ratio: 1/1; - margin-right: -11px; - padding: 8px; - display: flex; - align-items: center; + a:hover::after, + a:focus::after { + opacity: 1; + transform: translate3d(0, 0.2em, 0); } -} -.toast.active { - transform: translateY(0); + } diff --git a/ufund-ui/src/app/app.component.html b/ufund-ui/src/app/app.component.html index b41a225..959eada 100644 --- a/ufund-ui/src/app/app.component.html +++ b/ufund-ui/src/app/app.component.html @@ -1,11 +1,3 @@ -<div class="toast" #toastdiv> -<!-- <span{{(toast | async).message}}</span>--> -<!-- <a *ngIf="toast.action" (click)="toast.action.onAction()">{{toast.action.label}}</a>--> -<!-- <button (click)="toastdiv.classList.remove('active')">--> -<!-- <span class="icon">close</span>--> -<!-- </button>--> -</div> - <div id="header"> <div> <a routerLink="/"> @@ -17,8 +9,8 @@ <a routerLink="/cupboard">Cupboard</a> <a routerLink="/basket">Basket</a> <!-- <span>{{currentUser$ | async}}</span>--> - <button *ngIf="currentUser$.value != 'Logged out.'" onclick="location.href='/';"> Log Out</button> - <button *ngIf="currentUser$.value == 'Logged out.'" routerLink="/login"> Log In</button> + <button *ngIf="currentUser | async" (click)="logout()"> Log Out</button> + <button *ngIf="!(currentUser | async)" (click)="login()"> Log In</button> </div> </div> diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index 7d9afcd..2f98334 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -1,12 +1,10 @@ -import {Component, OnInit, Inject, ViewChild} from '@angular/core'; -import {BehaviorSubject, Observable} from 'rxjs'; +import {Component, OnInit, Inject, ViewContainerRef} from '@angular/core'; +import {BehaviorSubject} from 'rxjs'; import { DOCUMENT } from '@angular/common'; import {AuthService} from './services/auth.service'; -import {ToastType} from './services/toasts.service'; - -interface ToastProps { - type: ToastType, message: string, action?: {label: string, onAction: () => void} -} +import {ToastsService} from './services/toasts.service'; +import {User} from './models/User'; +import {ActivatedRoute, Router} from '@angular/router'; @Component({ selector: 'app-root', @@ -16,12 +14,14 @@ interface ToastProps { }) export class AppComponent implements OnInit { // title = 'ufund-ui'; - currentUser$: BehaviorSubject<string> = new BehaviorSubject<string>("Logged out."); - toast = new BehaviorSubject<ToastProps>({type: ToastType.INFO, message: "testToast"}) - + currentUser?: BehaviorSubject<User | null>; constructor( private authService: AuthService, + private router: Router, + private route: ActivatedRoute, + protected toastService: ToastsService, + private viewContainerRef: ViewContainerRef, @Inject(DOCUMENT) private document: Document ) {} @@ -30,14 +30,22 @@ export class AppComponent implements OnInit { } ngOnInit() { - this.authService.getCurrentUserSubject().subscribe(r => { - this.currentUser$?.next(r - ? "Logged in as " + r.username - : "Logged out." - ) - }) + this.toastService.setRootViewContainerRef(this.viewContainerRef) + this.currentUser = this.authService.getCurrentUserSubject() + let data = localStorage.getItem("credential"); + if (data) { + let dataParsed = JSON.parse(data) + this.authService.restoreLogin(dataParsed.username, dataParsed.key) + console.log("Key found", dataParsed.key) + } } + login() { + this.router.navigate(['/login'], {queryParams: {redir: this.router.url}}); + } - + logout() { + localStorage.removeItem("credential") + location.reload() + } } diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index ea7e6ad..c91256e 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -16,6 +16,7 @@ import {CommonModule} from '@angular/common'; import {LoginComponent} from './components/login/login.component'; import { MiniNeedListComponent } from './components/mini-need-list/mini-need-list.component'; import { SignupComponent } from './components/signup/signup.component'; +import { ToastComponent } from './components/toast/toast.component'; @NgModule({ declarations: [ @@ -29,6 +30,7 @@ import { SignupComponent } from './components/signup/signup.component'; LoginComponent, SignupComponent, MiniNeedListComponent, + ToastComponent, ], imports: [ BrowserModule, diff --git a/ufund-ui/src/app/components/dashboard/dashboard.component.ts b/ufund-ui/src/app/components/dashboard/dashboard.component.ts index 165c7ba..645893f 100644 --- a/ufund-ui/src/app/components/dashboard/dashboard.component.ts +++ b/ufund-ui/src/app/components/dashboard/dashboard.component.ts @@ -1,10 +1,8 @@ import {Component, OnInit} from '@angular/core'; -import {userType} from '../../models/User'; import {AuthService} from '../../services/auth.service'; import {Router} from '@angular/router'; import {Need} from '../../models/Need'; import {CupboardService} from '../../services/cupboard.service'; -import {UsersService} from '../../services/users.service'; import {firstValueFrom} from 'rxjs'; @Component({ @@ -27,7 +25,7 @@ export class DashboardComponent implements OnInit{ ngOnInit() { let user = this.authService.getCurrentUser() - if(!user) { + if(!localStorage.getItem("credential") && !user) { this.router.navigate(['/login']) return } @@ -35,7 +33,7 @@ export class DashboardComponent implements OnInit{ firstValueFrom(this.cupboardService.getNeeds()).then(r => { this.topNeeds = r.sort((a, b) => b.current - a.current) this.almostThere = r.sort((a, b) => a.current/a.maxGoal - b.current/b.maxGoal) - this.inBasket = r.filter(n => n.id in user?.basket) + this.inBasket = r.filter(n => n.id in user!.basket) }) } diff --git a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts index faa7e0b..24e2c0b 100644 --- a/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts +++ b/ufund-ui/src/app/components/funding-basket/funding-basket.component.ts @@ -4,6 +4,7 @@ import {Router} from '@angular/router'; import {CupboardService} from '../../services/cupboard.service'; import {catchError, firstValueFrom, Observable} from 'rxjs'; import {AuthService} from '../../services/auth.service'; +import {ToastsService, ToastType} from '../../services/toasts.service'; @Component({ selector: 'app-funding-basket', @@ -18,7 +19,8 @@ export class FundingBasketComponent implements OnInit { private router: Router, protected cupboardService: CupboardService, protected usersService: UsersService, - private authService: AuthService + private authService: AuthService, + private toastService: ToastsService ) {} @ViewChild("contribution") contribution?: Input; @@ -42,9 +44,20 @@ export class FundingBasketComponent implements OnInit { contribution.setAttribute("style", ""); if (contribution.value == '' || contribution.valueAsNumber <= 0) { this.isValid = false; + contribution.setAttribute("style", "color: #ff0000"); + this.toastService.sendToast(ToastType.WARNING, "Invalid input in funding basket!") } } + // if (this.usersService.getBasket().value != await firstValueFrom(this.usersService.getUser(1)) + // for (let c of this.usersService.getBasket().value) { + // if (c == null) { + // this.isValid = false; + // this.statusText.next("One or more needs have been deleted") + // } else { + // this.statusText.next("test") + // } + // } if (this.isValid) { for (let c of document.getElementById("funding-basket")?.querySelectorAll('.contribution')!) { let contribution = c as HTMLInputElement; @@ -54,11 +67,11 @@ export class FundingBasketComponent implements OnInit { this.cupboardService.checkoutNeed(need.id, +contribution.value) .pipe(catchError((ex, _) => { if (ex.status == 500) { - this.statusText.next('Fields cannot be blank'); + this.toastService.sendToast(ToastType.INFO, 'Fields cannot be blank'); } else if (ex.status == 400) { - this.statusText.next('Goal must be greater than 0'); + this.toastService.sendToast(ToastType.INFO, 'Goal must be greater than 0'); } else { - this.statusText.next('Error on creating need'); + this.toastService.sendToast(ToastType.INFO, 'Error on creating need'); } return new Observable<string>(); })) diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index 743b1b3..e1c3e2a 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,5 +1,4 @@ <div id="box"> - <span *ngIf="next" style="color: red">You must be logged in to view this page</span> <h1>Login</h1> <input placeholder="Username" type="text" #username> <input placeholder="Password" type="password" #password> diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index f6a2996..4dcaedd 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -35,6 +35,8 @@ export class LoginComponent implements OnInit { this.authService.login(username, password).then(() => { this.router.navigate([next]); + let key = this.authService.getApiKey() + localStorage.setItem("credential", JSON.stringify({username: username, key: key})) }).catch(ex => { this.statusText.next("Unable to login: " + friendlyHttpStatus[ex.status]) console.log(ex) diff --git a/ufund-ui/src/app/components/need-list/need-list.component.html b/ufund-ui/src/app/components/need-list/need-list.component.html index d35f2ed..c325320 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.html +++ b/ufund-ui/src/app/components/need-list/need-list.component.html @@ -41,7 +41,7 @@ <div class="prog"> <span id="hover-status-label-{{need.id}}"> </span> - <span>{{need.current}}/{{need.maxGoal}} ({{(need.current / need.maxGoal) * 100}}%)</span> + <span>{{need.current}}/{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span> <progress [value]="need.current" [max]="need.maxGoal"></progress> </div> diff --git a/ufund-ui/src/app/components/toast/toast.component.css b/ufund-ui/src/app/components/toast/toast.component.css new file mode 100644 index 0000000..4cd81fe --- /dev/null +++ b/ufund-ui/src/app/components/toast/toast.component.css @@ -0,0 +1,57 @@ +:host { + display: flex; + align-items: center; + justify-content: center; +} + +@keyframes slideDown { + from {transform: translateY(-90px);} + to {transform: translateY(0);} +} + +.toast { + /*transform: translateY(-90px);*/ + animation: slideDown .5s ease-in-out; + transition: transform .5s; + align-self: center; + z-index: 3; + position: absolute; + top: 15px; + display: flex; + flex-direction: row; + padding: 3px 15px; + background-color: #3a3a3a; + border-radius: 100000px; + gap: 10px; + align-items: center; + + button { + aspect-ratio: 1/1; + margin-right: -11px; + padding: 8px; + display: flex; + align-items: center; + background-color: transparent; + } + + button:hover { + background-color: rgba(255, 255, 255, 0.1); + } +} + +.toast.hide { + transform: translateY(-90px); +} + +.toast.warning { + background-color: #ffc500; + color: black; + + button { + color: black; + } +} + +.toast.error { + background-color: #d81a1a; +} diff --git a/ufund-ui/src/app/components/toast/toast.component.html b/ufund-ui/src/app/components/toast/toast.component.html new file mode 100644 index 0000000..dccf869 --- /dev/null +++ b/ufund-ui/src/app/components/toast/toast.component.html @@ -0,0 +1,7 @@ +<div class="toast" [ngClass]="ToastType[type].toLowerCase()" #toastDiv> + <span>{{this.message}}</span> + <a *ngIf="this.action" (click)="this.action.onAction()">{{this.action.label}}</a> + <button (click)="hide()"> + <span class="icon">close</span> + </button> +</div> diff --git a/ufund-ui/src/app/components/toast/toast.component.ts b/ufund-ui/src/app/components/toast/toast.component.ts new file mode 100644 index 0000000..47fd7ff --- /dev/null +++ b/ufund-ui/src/app/components/toast/toast.component.ts @@ -0,0 +1,37 @@ +import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core'; +import {ToastType} from '../../services/toasts.service'; + +@Component({ + selector: 'app-toast', + standalone: false, + templateUrl: './toast.component.html', + styleUrl: './toast.component.css' +}) +export class ToastComponent implements OnInit{ + @Input() type!: ToastType + @Input() message!: string + @Input() action?: {label: string, onAction: () => void} + + @ViewChild("toastDiv") toastDiv!: ElementRef<HTMLDivElement> + + ngOnInit() { + setTimeout(() => { + this.hide(); + }, 3000) + } + + hide() { + console.log(this.toastDiv, typeof this.toastDiv) + this.toastDiv.nativeElement.classList.add('hide') + } + + getColor() { + switch (this.type) { + case ToastType.ERROR: return "red"; + case ToastType.INFO: return ""; + case ToastType.WARNING: return "yellow"; + } + } + + protected readonly ToastType = ToastType; +} diff --git a/ufund-ui/src/app/services/auth.service.ts b/ufund-ui/src/app/services/auth.service.ts index 6bc7145..b75c931 100644 --- a/ufund-ui/src/app/services/auth.service.ts +++ b/ufund-ui/src/app/services/auth.service.ts @@ -1,7 +1,8 @@ -import {Injectable} from '@angular/core'; +import {Injectable, Injector} from '@angular/core'; import {BehaviorSubject, firstValueFrom} from 'rxjs'; import {User} from '../models/User'; import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {UsersService} from './users.service'; @Injectable({ providedIn: 'root' @@ -24,7 +25,9 @@ export class AuthService { }); constructor( - private http: HttpClient + private http: HttpClient, + // private userService: UsersService + private injector: Injector ) {} async login(username: string, password: string) { @@ -42,6 +45,13 @@ export class AuthService { // this.currentUser.subscribe(r => console.log("currentUser: "+r.username)) } + async restoreLogin(username: string, key: string) { + + const userService = this.injector.get(UsersService); + this.apiKey = key; + this.currentUser.next(await firstValueFrom(userService.getUser(username))) + } + getCurrentUserSubject() { return this.currentUser; } diff --git a/ufund-ui/src/app/services/toasts.service.ts b/ufund-ui/src/app/services/toasts.service.ts index 0c35e45..4fd024e 100644 --- a/ufund-ui/src/app/services/toasts.service.ts +++ b/ufund-ui/src/app/services/toasts.service.ts @@ -1,4 +1,5 @@ -import {Injectable} from '@angular/core'; +import {Injectable, ViewContainerRef} from '@angular/core'; +import {ToastComponent} from '../components/toast/toast.component'; export enum ToastType { INFO, @@ -11,9 +12,16 @@ export enum ToastType { }) export class ToastsService { - constructor() {} + private vcr?: ViewContainerRef sendToast(type: ToastType, message: string, action?: {label: string, onAction: () => void}) { + let compRef = this.vcr?.createComponent(ToastComponent)! + compRef.setInput("message", message) + compRef.setInput("type", type) + compRef.setInput("action", action) + } + setRootViewContainerRef(vcr: ViewContainerRef) { + this.vcr = vcr } } diff --git a/ufund-ui/src/styles.css b/ufund-ui/src/styles.css index b3ecbd0..b152e61 100644 --- a/ufund-ui/src/styles.css +++ b/ufund-ui/src/styles.css @@ -32,7 +32,7 @@ input { } } -button, .button { +button, input[type=button], input[type=reset], input[type=submit], .button { font-size: 14pt; padding: 6px 16px; border-radius: 9999px; |