diff options
Diffstat (limited to 'ufund-ui/src/app/components/need-list')
3 files changed, 87 insertions, 343 deletions
diff --git a/ufund-ui/src/app/components/need-list/need-list.component.css b/ufund-ui/src/app/components/need-list/need-list.component.css index 56ae6a6..b3af85f 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.css +++ b/ufund-ui/src/app/components/need-list/need-list.component.css @@ -1,9 +1,3 @@ -#header { -    display: flex; -    flex-direction: column; -    gap: 10px -} -  .needEntry {      background-color: var(--tertiary-color);      display: flex; @@ -17,33 +11,6 @@      gap: 15px  } -select { -    font-size: 14pt; -    padding: 5px; -} - -#searchArea { -    display: flex; - -    form { -        display: flex; -        width: 100%; -        gap: 10px; -    } - -    input[type=text] { -        display: flex; -        width: 100%; -    } -} - -#sortArea { -    display: flex; -    flex-direction: row; -    gap: 10px; -    align-items: center; -} -  .needName {      font-weight: bold;  } @@ -53,18 +20,49 @@ select {      font-size: 10pt;  } +.need-image { +    transition: all 0.15s ease-in-out; +    height: 130px; +    width: 200px; +    margin: -10px 0 0 -10px; +    object-fit: cover; +    border-radius: 5px; +    mask-image: linear-gradient(to right, rgb(255,255,255) 0, rgb(255,255,255,.1) 60%, rgb(255,255,255,0) 100%); +} + +/*.clickable:hover {*/ +/*    .need-image {*/ +/*        mask-image: none;*/ +/*        width: 210px*/ +/*    }*/ +/*    .left {*/ +/*        width: 32.5%;*/ +/*    }*/ +/*    .prog {*/ +/*        width: 72.5%;*/ +/*    }*/ +/*}*/ +  .split {      display: flex;      flex-direction: row; -    justify-content: space-between; -      .left { +        width: 15%; +        transition: all 0.2s ease-in-out; +        display: flex; +        flex-direction: column; +    } + +    .middle { +        width: 42.5%;          display: flex; +        align-items: start;          flex-direction: column;      }      .right { +        width: 42.5%;          display: flex;          flex-direction: column;          align-items: end; @@ -80,8 +78,12 @@ select {  }  .prog { +    transition: all 0.2s ease-in-out; +    width: 85%;      display: flex;      flex-direction: column; +    align-self: end; +    margin-top: -5.25%;  }  .clickable { @@ -105,6 +107,10 @@ select {      gap: 5px;  } +.actionArea:empty { +    padding: 0; +} +  #page-selector {      display: flex;      align-items: center; 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 e9f70f6..0e5b762 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 @@ -1,39 +1,17 @@ -<div id="header"> -    <div id="searchArea"> -        <form id="search-form" #searchForm="ngForm"> -            <input type="text" name="search" class="wide-input sort-scheme" placeholder="Search in {{needs.length}} needs..." (input)="search(searchForm.value)" ngModel> -            <input type="reset" value="Clear" (click)="search(null)"> <br> -        </form> -    </div> -    <div id="sortArea"> -        <label for="sort">Sort by: </label> -        <select [(ngModel)] = "sortSelection" class="wide-input sort-scheme" (change)="search(searchForm.value)" [value]="sortSelection"> -            <option *ngFor="let algorithm of SortingAlgoArrays" value="{{algorithm.name}}"> -                {{algorithm.display[sortMode === 'Ascending' ? 0 : 1]}} -            </option> -        </select> -        <button (click)="changeSortMode(searchForm.value)"> -            <span class="icon">{{sortMode === 'Ascending' ? 'arrow_upward': 'arrow_downward'}}</span> -        </button> -        <label>Needs per page: </label> -        <input class="sort-scheme" type ="number" [(ngModel)]="itemsPerPage" (change)="editNeedsPerPage()" min="1" max="{{searchResults.length}}"> -    </div> -    <!--<button (click)="close()">Close</button>--> -</div> -  <!-- display for when results are present and filtered--> -<h2 *ngIf="searchResults.length < needs.length && searchResults.length != 0"> Search Results({{needs.length - searchResults.length}} needs filtered): </h2> -<h2 *ngIf="searchResults.length == needs.length"> All Needs </h2> -<h2 *ngIf="searchResults.length == 0"> No Results Found </h2>  <div id="needList">      <div *ngFor="let need of visibleNeeds" class="needEntry">          <div [routerLink]="'/need/' + need.id" class="clickable"> +              <div class="split">                  <div class="left"> +                    <img *ngIf="need.image" alt="Need image" class="need-image" [src]="need.image"/> +                </div> + +                <div class="middle">                      <span class="needName">{{need.name}}</span>                      <span class="needType">{{need.type}}</span>                  </div> -                  <div class="right">                      <span *ngIf="need.urgent" class="urgent">URGENT</span>                      <span *ngIf="need.location"><span class="icon">location_on</span>{{need.location}}</span> @@ -46,19 +24,10 @@                  <span>{{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.current}}/{{need.type.toString() == 'MONETARY' ? '$' : ''}}{{need.maxGoal}} ({{((need.current / need.maxGoal) * 100).toFixed(0)}}%)</span>                  <progress [value]="need.current" [max]="need.maxGoal"></progress>              </div> -          </div> -        <div class="actionArea"> -            <button *ngIf="isHelper()" (click)="add(need)"> -                <span class="icon">add</span>Add To Basket -            </button> -            <button *ngIf="isManager()" (click)="select(need)"> -                <span class="icon">edit</span>Edit Need -            </button> -            <button *ngIf="isManager()" (click)="delete(need.id)" > -                <span class="icon">delete</span>Delete Need -            </button> +        <div *ngIf="actionArea" class="actionArea"> +            <ng-container [ngTemplateOutlet]="actionArea" [ngTemplateOutletContext]="{$implicit: need}"/>          </div>      </div>  </div> diff --git a/ufund-ui/src/app/components/need-list/need-list.component.ts b/ufund-ui/src/app/components/need-list/need-list.component.ts index ae6bc99..7ca0ae7 100644 --- a/ufund-ui/src/app/components/need-list/need-list.component.ts +++ b/ufund-ui/src/app/components/need-list/need-list.component.ts @@ -1,293 +1,62 @@ -import {Component, EventEmitter, Output} from '@angular/core'; +import {Component, Input, OnChanges, TemplateRef} from '@angular/core';  import {GoalType, Need} from '../../models/Need'; -import {CupboardService} from '../../services/cupboard.service'; -import {UsersService} from '../../services/users.service'; -import {userType} from '../../models/User'; -import {AuthService} from '../../services/auth.service'; -import {catchError, of} from 'rxjs'; -import {ToastsService, ToastType} from '../../services/toasts.service'; - -interface sortAlgo { -  (a: Need,b: Need): number; -} - -// sort functions -const sortByName: sortAlgo = (a: Need, b: Need): number => { -  if(a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { -    return -1; -  } -  return 1; -} - -const sortByGoal: sortAlgo = (a: Need, b: Need): number => { -  if(a.maxGoal == b.maxGoal) { -    return sortByName(a,b); -  } -  else if(a.maxGoal > b.maxGoal) { -    return -1; -  } -  return 1; -} - -const sortByCompletion: sortAlgo = (a: Need, b: Need): number => { -  if(a.current == b.current) { -    return sortByGoal(a,b); -  } -  else if(a.current > b.current) { -    return -1; -  } -  return 1; -} - -const sortByPriority: sortAlgo = (a: Need, b: Need): number => { -  if(a.urgent == b.urgent) { -    return sortByGoal(a,b); -  } -  else if(a.urgent && !b.urgent) { -    return -1; -  } -  return 1; -} - -const sortByLocation: sortAlgo = (a: Need, b: Need): number => { -  if(a.location.toLocaleLowerCase() < b.location.toLocaleLowerCase()) { -    return -1; -  } -  return 1; -}  @Component({ -  selector: 'app-need-list', -  standalone: false, -  templateUrl: './need-list.component.html', -  styleUrl: './need-list.component.css' +    selector: 'app-need-list', +    standalone: false, +    templateUrl: './need-list.component.html', +    styleUrl: './need-list.component.css'  }) -export class NeedListComponent { -  selectedNeed: Need | null = null; -  needs: Need[] = []; -  searchResults: Need[] = []; -  visibleNeeds: Need[] = []; -  sortMode: 'Ascending' | 'Descending' = 'Ascending' -  currentPage: number = 0; -  itemsPerPage: number = 5; -  totalPages: number = Math.ceil(this.needs.length / this.itemsPerPage); - -  getPrefix(need: Need) { -      return (need.type === GoalType.MONETARY) ? "$" : ""; -  } +export class NeedListComponent implements OnChanges { -  decrementPage() { -    this.currentPage--; -    this.updateVisibleNeeds(); -  } +    @Input({required: true}) needs!: Need[] +    @Input({required: true}) uid!: number +    @Input() itemsPerPage: number = 5; +    @Input() actionArea: TemplateRef<any> | null = null -  incrementPage() { -    this.currentPage++; -    this.updateVisibleNeeds(); -  } +    visibleNeeds: Need[] = []; +    currentPage: number = parseInt(localStorage.getItem('currentPage'+this.uid) ?? '0') ?? 0; +    totalPages: number = 0; -  lastPage() { -      this.currentPage = this.totalPages - 1 -      this.updateVisibleNeeds() -  } - -  firstPage() { -      this.currentPage = 0 -      this.updateVisibleNeeds() -  } - -  editNeedsPerPage() { -    if (this.itemsPerPage > this.searchResults.length) { -      this.itemsPerPage = this.searchResults.length; +    ngOnChanges() { +        this.updateVisibleNeeds() +        this.currentPage = parseInt(localStorage.getItem('currentPage'+this.uid) ?? '0') ?? 0;      } -    if (this.itemsPerPage < 1) { -      this.itemsPerPage = 1; -    } -    this.resetVisibleNeeds(); -  } - -  updateVisibleNeeds() { -    this.totalPages = Math.ceil(this.searchResults.length / this.itemsPerPage); -    this.visibleNeeds = this.searchResults.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage); -  } -  resetVisibleNeeds() { -    this.currentPage = 0; -    this.updateVisibleNeeds(); -  } - -  currentSortAlgo: sortAlgo = sortByPriority; -  sortSelection: string = 'sortByPriority'; - -  SortingAlgoArrays: {func:sortAlgo,name:string, display:string[]}[] = [ -    {func:sortByPriority,name:"sortByPriority", display:["Highest Priority", "Lowest Priority"]}, -    {func:sortByName,name:"sortByName", display:["Name (A to Z)", "Name (Z to A)"]}, -    {func:sortByLocation,name:"sortByLocation", display:["Location (A to Z)", "Location (Z to A)"]}, -    {func:sortByCompletion,name:"sortByCompletion", display:["Most Completed", "Least Completed"]}, -    {func:sortByGoal,name:"sortByGoal", display:["Largest Maximum Goal", "Smallest Maximum Goal"]}, -  ]; - -  @Output() currentNeed = new EventEmitter<Need>(); - -  constructor( -    private cupboardService: CupboardService, -    private usersService: UsersService, -    private authService: AuthService, -    private toastService: ToastsService -  ) {} - -    refresh() { -        this.cupboardService.getNeeds().subscribe(n => { -          if (this.sortMode == 'Ascending') { -            this.needs = n.sort(this.currentSortAlgo); -          } else { -            this.needs = n.sort(this.currentSortAlgo).reverse(); -          } -          this.searchResults = this.needs; -          this.updateVisibleNeeds(); -        }); - -        const form = document.getElementById('search-form') as HTMLFormElement; -        form.reset(); -        this.search(null); +    getPrefix(need: Need) { +        return (need.type === GoalType.MONETARY) ? "$" : "";      } -  ngOnInit(): void { -    this.refresh() - -  } - -  changeSortMode(form : any) { -    if (this.sortMode == 'Ascending'){ -      this.sortMode = 'Descending' -    } else { -      this.sortMode = 'Ascending' +    //increment/decrement +    decrementPage() { +        this.currentPage--; +        localStorage.setItem('currentPage'+this.uid, this.currentPage.toString()); +        this.updateVisibleNeeds();      } -    this.search(form) -  } -  private searchDelay: any; - -  async search(form: any) { -    //wait .25 seconds before searching but cancel if another search is made during the wait to prevent too many api calls - -    //remove previous search if it exists -    if (this.searchDelay) { -      clearTimeout(this.searchDelay); +    incrementPage() { +        this.currentPage++; +        localStorage.setItem('currentPage'+this.uid, this.currentPage.toString()); +        this.updateVisibleNeeds();      } -    if (form) { -      this.searchDelay = setTimeout(() => { - -        if (form) { -          //sorting based on algo selected -          this.SortingAlgoArrays.forEach(algo => { -            if(algo.name === this.sortSelection) { -              this.currentSortAlgo = algo.func; -              console.log("changed sorting algorithm to: ", algo.name + this.sortMode) -              return -            } -          }); - -          const currentSearchValue = form.search; //latest value of the search -          this.cupboardService.searchNeeds(currentSearchValue).subscribe((n) => { -            if (this.sortMode == 'Ascending') { -              this.searchResults = n.sort(this.currentSortAlgo); -            } else { -              this.searchResults = n.sort(this.currentSortAlgo).reverse(); -            } -            this.updateVisibleNeeds(); -            }); -          } -        }, 250); -      } else { -        //user has cleared the search bar, we can skip the timeout for a 1/4 second faster response -        //clear timeout to stop pending search -        clearTimeout(this.searchDelay); -        this.searchResults = this.needs; -      } -  } -  delete(id : number) { -    this.cupboardService.deleteNeed(id).subscribe(() => { -        this.toastService.sendToast(ToastType.INFO, "Need deleted.") -      this.needs = this.needs.filter(n => n.id !== id) -    }) -    this.refresh(); -  } - -  isManager() { -    const type = this.authService.getCurrentUser()?.type; -    return type === ("MANAGER" as unknown as userType); -  } - -  isHelper() { -    const type = this.authService.getCurrentUser()?.type; -    return type === ("HELPER" as unknown as userType); -  } - -  changeText(id : number, text : string) { -    const span = document.getElementById('hover-status-label-' + id); -    if (span) { -      span.innerHTML = ' ' + text; +    //skipping pages +    lastPage() { +        this.currentPage = this.totalPages - 1 +        localStorage.setItem('currentPage'+this.uid, this.currentPage.toString()); +        this.updateVisibleNeeds()      } -  } -  add(need: Need) { -    const currentUser = this.authService.getCurrentUser(); -    //console.log("get current user in angular:", currentUser) -    if (currentUser) { -      if (!currentUser.basket.includes(need.id)) { -        currentUser.basket.push(need.id); -        this.toastService.sendToast(ToastType.INFO, "Need added to your basket!") -        this.usersService.updateUser(currentUser) -            .pipe(catchError((err, _) =>  { -                console.error(err); -                return of(); -            })) -            .subscribe(() => { -                this.usersService.refreshBasket(); -            }); -      } else { -        this.toastService.sendToast(ToastType.ERROR, "This need is already in your basket!") -      } +    firstPage() { +        this.currentPage = 0 +        localStorage.setItem('currentPage'+this.uid, this.currentPage.toString()); +        this.updateVisibleNeeds()      } -  } - -  back() { -    this.searchResults = this.needs; -  } -  select(need : Need) { -    //emit value -    this.currentNeed.emit(need); -    if (this.selectedNeed) { -      //revert already selected need to previous style -      console.log(need.id); -      let button = document.getElementById('need-button-' + this.selectedNeed.id); -      if (button) { -        console.log(button) -        button.style.background = 'lightgray'; -        button.style.marginLeft = '0%'; -        button.style.width = '98%'; -      } -      button = document.getElementById('need-edit-button-' + this.selectedNeed.id); -      if (button) { -        button.style.visibility = 'visible'; -      } +    updateVisibleNeeds() { +        this.totalPages = Math.ceil(this.needs.length / this.itemsPerPage); +        this.visibleNeeds = this.needs.slice(this.currentPage * this.itemsPerPage, (this.currentPage + 1) * this.itemsPerPage);      } -      //change selected need to selected style -      this.selectedNeed = need; -      let button = document.getElementById('need-button-' + need.id); -      if (button) { -        button.style.background = 'white'; -        button.style.marginLeft = '4%'; -        button.style.width = '100%'; -      } -      button = document.getElementById('need-edit-button-' + need.id); -      if (button) { -        button.style.visibility = 'hidden'; -      } -  }      protected readonly GoalType = GoalType;  }  | 
