Ignore:
Timestamp:
10/22/21 21:46:23 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
8b6791f
Parents:
81c2e6f
Message:

Preview questions and search

Location:
src/Clients/Angular/finki-chattery/src
Files:
13 added
19 edited

Legend:

Unmodified
Added
Removed
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts

    r81c2e6f r70e04f1  
    55import { catchError, filter, map } from 'rxjs/operators';
    66
    7 import { QuestionStateViewModel } from 'src/app/shared-app/models';
    8 import { EffectStartedWorking, GetQuestionState } from './question-state/question.actions';
     7import { PreviewQuestionsOrderEnum, PreviewQuestionViewModel, QuestionStateViewModel } from 'src/app/shared-app/models';
     8import {
     9  EffectStartedWorking,
     10  GetPreviewQuestionsLatest,
     11  GetPreviewQuestionsPopular,
     12  GetQuestionState
     13} from './question-state/question.actions';
    914import { questionStateQuery } from './question-state/question.selectors';
    1015import { QuestionState } from './question-state/question.state';
     
    3843  }
    3944
     45  public getPreviewQuestionsLatest(): Observable<PreviewQuestionViewModel[]> {
     46    return this.store.select(questionStateQuery.getPreviewQuestionsLatest);
     47  }
     48
     49  public getPreviewQuestionsPopular(): Observable<PreviewQuestionViewModel[]> {
     50    return this.store.select(questionStateQuery.getPreviewQuestionsPopular);
     51  }
     52
     53  public fetchPreviewQuestions(orderBy: PreviewQuestionsOrderEnum): void {
     54    if (orderBy === PreviewQuestionsOrderEnum.Latest) {
     55      this.fetchPreviewQuestionsLatest();
     56    } else if (orderBy === PreviewQuestionsOrderEnum.Popular) {
     57      this.fetchPreviewQuestionsPopular();
     58    }
     59  }
     60
    4061  public fetchQuestion(questionUid: string): void {
    4162    this.dispatchEffect(new GetQuestionState(questionUid));
     63  }
     64
     65  private fetchPreviewQuestionsLatest(): void {
     66    this.dispatchEffect(new GetPreviewQuestionsLatest());
     67  }
     68
     69  private fetchPreviewQuestionsPopular(): void {
     70    this.dispatchEffect(new GetPreviewQuestionsPopular());
    4271  }
    4372
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question-state.models.ts

    r81c2e6f r70e04f1  
    5959  public reputation!: number;
    6060}
     61
     62export class PreviewQuestionResponse {
     63  public uid!: string;
     64  public title!: string;
     65  public createdOn!: moment.Moment;
     66  public views!: number;
     67  public answersCount!: number;
     68  public categories!: PreviewQuestionCategoryResponse[];
     69}
     70
     71export class PreviewQuestionCategoryResponse {
     72  public uid!: string;
     73  public text!: string;
     74}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts

    r81c2e6f r70e04f1  
    22import { Action } from '@ngrx/store';
    33
    4 import { QuestionStateViewModel } from 'src/app/shared-app/models';
     4import { PreviewQuestionViewModel, QuestionStateViewModel } from 'src/app/shared-app/models';
     5import { PreviewQuestionResponse } from './question-state.models';
    56
    67export enum QuestionActionTypes {
    78  GetQuestionState = '[Question] Get state',
    89  GetQuestionStateSuccess = '[Question] Get state success',
     10  GetPreviewQuestionsLatest = '[Question] Get preview questions Latest',
     11  GetPreviewQuestionsLatestSuccess = '[Question] Get preview questions Latest Success',
     12  GetPreviewQuestionsPopular = '[Question] Get preview questions Popular',
     13  GetPreviewQuestionsPopularSuccess = '[Question] Get preview questions Popular Success',
    914  EffectStartedWorking = '[Question] Effect Started Working',
    1015  EffectFinishedWorking = '[Question] Effect Finished Working',
     
    2227
    2328  constructor(public payload: QuestionStateViewModel) {}
     29}
     30
     31export class GetPreviewQuestionsLatest implements Action {
     32  readonly type = QuestionActionTypes.GetPreviewQuestionsLatest;
     33
     34  constructor() {}
     35}
     36
     37export class GetPreviewQuestionsLatestSuccess implements Action {
     38  readonly type = QuestionActionTypes.GetPreviewQuestionsLatestSuccess;
     39
     40  constructor(public payload: PreviewQuestionViewModel[]) {}
     41}
     42
     43export class GetPreviewQuestionsPopular implements Action {
     44  readonly type = QuestionActionTypes.GetPreviewQuestionsPopular;
     45
     46  constructor() {}
     47}
     48
     49export class GetPreviewQuestionsPopularSuccess implements Action {
     50  readonly type = QuestionActionTypes.GetPreviewQuestionsPopularSuccess;
     51
     52  constructor(public payload: PreviewQuestionViewModel[]) {}
    2453}
    2554
     
    4271}
    4372
    44 export type QuestionAction = GetQuestionStateSuccess | EffectStartedWorking | EffectFinishedWorking | EffectFinishedWorkingError;
     73export type QuestionAction =
     74  | GetQuestionStateSuccess
     75  | GetPreviewQuestionsLatestSuccess
     76  | GetPreviewQuestionsPopularSuccess
     77  | EffectStartedWorking
     78  | EffectFinishedWorking
     79  | EffectFinishedWorkingError;
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts

    r81c2e6f r70e04f1  
    11import { Injectable } from '@angular/core';
    22import { Actions, createEffect, ofType } from '@ngrx/effects';
    3 import { catchError, switchMap } from 'rxjs/operators';
     3import { catchError, filter, switchMap, withLatestFrom } from 'rxjs/operators';
     4import { PreviewQuestionsOrderEnum } from 'src/app/shared-app/models';
     5import { TranslateFromJsonService } from 'src/app/shared-app/services';
    46
    57import { BaseApiService } from 'src/app/shared-app/services/base-api.service';
    6 import { QuestionStateResponse } from './question-state.models';
     8import { QuestionFacadeService } from '../question-facade.service';
     9import { PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';
    710import {
    811  EffectFinishedWorking,
    912  EffectFinishedWorkingError,
     13  GetPreviewQuestionsLatestSuccess,
     14  GetPreviewQuestionsPopularSuccess,
    1015  GetQuestionState,
    1116  GetQuestionStateSuccess,
     
    1823})
    1924export class QuestionEffects {
    20   constructor(private actions$: Actions, private api: BaseApiService) {}
     25  constructor(
     26    private actions$: Actions,
     27    private api: BaseApiService,
     28    private translate: TranslateFromJsonService,
     29    private facade: QuestionFacadeService
     30  ) {}
    2131
    2232  getQuestionState$ = createEffect(() => {
     
    3141    );
    3242  });
     43
     44  getPreviewQuestionsLatest$ = createEffect(() => {
     45    return this.actions$.pipe(
     46      ofType<GetQuestionState>(QuestionActionTypes.GetPreviewQuestionsLatest),
     47      withLatestFrom(this.facade.getPreviewQuestionsLatest()),
     48      filter(([action, questions]) => questions.length === 0),
     49      switchMap((action) => {
     50        return this.api.get<PreviewQuestionResponse[]>(`v1/questions/preview?order=${PreviewQuestionsOrderEnum.Latest}`).pipe(
     51          switchMap((state) => [
     52            new GetPreviewQuestionsLatestSuccess(QuestionMapper.ToPreviwQuestionsViewModel(state, this.translate)),
     53            new EffectFinishedWorking()
     54          ]),
     55          catchError((err) => [new EffectFinishedWorkingError(err)])
     56        );
     57      })
     58    );
     59  });
     60
     61  getPreviewQuestionsPopular$ = createEffect(() => {
     62    return this.actions$.pipe(
     63      ofType<GetQuestionState>(QuestionActionTypes.GetPreviewQuestionsPopular),
     64      withLatestFrom(this.facade.getPreviewQuestionsPopular()),
     65      filter(([action, questions]) => questions.length === 0),
     66      switchMap((action) => {
     67        return this.api.get<PreviewQuestionResponse[]>(`v1/questions/preview?order=${PreviewQuestionsOrderEnum.Popular}`).pipe(
     68          switchMap((state) => [
     69            new GetPreviewQuestionsPopularSuccess(QuestionMapper.ToPreviwQuestionsViewModel(state, this.translate)),
     70            new EffectFinishedWorking()
     71          ]),
     72          catchError((err) => [new EffectFinishedWorkingError(err)])
     73        );
     74      })
     75    );
     76  });
    3377}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.mapper.ts

    r81c2e6f r70e04f1  
    55  AnswerResponseStudentQuestionStateViewModel,
    66  AnswerStudentQuestionStateViewModel,
     7  PreviewQuestionCategoryViewModel,
     8  PreviewQuestionViewModel,
    79  QuestionCategoryQuestionStateViewModel,
    810  QuestionStateViewModel,
     
    1012  TeamQuestionStateViewModel
    1113} from 'src/app/shared-app/models';
    12 import { QuestionStateResponse } from './question-state.models';
     14import { TranslateFromJsonService } from 'src/app/shared-app/services';
     15import { PreviewQuestionCategoryResponse, PreviewQuestionResponse, QuestionStateResponse } from './question-state.models';
    1316
    1417export class QuestionMapper {
     
    8487    );
    8588  }
     89
     90  public static ToPreviwQuestionsViewModel(
     91    previewQuestionsResponse: PreviewQuestionResponse[],
     92    translate: TranslateFromJsonService
     93  ): PreviewQuestionViewModel[] {
     94    let questions = new Array<PreviewQuestionViewModel>();
     95
     96    if (previewQuestionsResponse.length > 0) {
     97      questions = previewQuestionsResponse.map((x) => {
     98        let categories = new Array<PreviewQuestionCategoryViewModel>();
     99
     100        if (x.categories.length > 0) {
     101          categories = x.categories.map((y) => new PreviewQuestionCategoryViewModel(y.uid, y.text, translate.instant(y.text)));
     102        }
     103
     104        return new PreviewQuestionViewModel(x.uid, x.title, moment(x.createdOn), x.views, x.answersCount, categories);
     105      });
     106    }
     107
     108    return questions;
     109  }
    86110}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts

    r81c2e6f r70e04f1  
    88        ...state,
    99        question: action.payload
     10      };
     11    case QuestionActionTypes.GetPreviewQuestionsLatestSuccess:
     12      return {
     13        ...state,
     14        previewQuestionsLatest: action.payload
     15      };
     16    case QuestionActionTypes.GetPreviewQuestionsPopularSuccess:
     17      return {
     18        ...state,
     19        previewQuestionsPopular: action.payload
    1020      };
    1121    case QuestionActionTypes.EffectStartedWorking: {
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.selectors.ts

    r81c2e6f r70e04f1  
    55
    66const getQuestion = createSelector(getQuestionState, (state) => state.question);
     7const getPreviewQuestionsLatest = createSelector(getQuestionState, (state) => state.previewQuestionsLatest);
     8const getPreviewQuestionsPopular = createSelector(getQuestionState, (state) => state.previewQuestionsPopular);
    79const effectWorking = createSelector(getQuestionState, (state) => state.effectWorking);
    810
    911export const questionStateQuery = {
    1012  effectWorking,
    11   getQuestion
     13  getQuestion,
     14  getPreviewQuestionsLatest,
     15  getPreviewQuestionsPopular
    1216};
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.state.ts

    r81c2e6f r70e04f1  
    11import { HttpErrorResponse } from '@angular/common/http';
    2 import { QuestionStateViewModel } from 'src/app/shared-app/models';
     2import { PreviewQuestionViewModel, QuestionStateViewModel } from 'src/app/shared-app/models';
    33
    44export const questionStateKey = 'question';
     
    66export interface QuestionState {
    77  question: QuestionStateViewModel | null;
     8  previewQuestionsLatest: PreviewQuestionViewModel[];
     9  previewQuestionsPopular: PreviewQuestionViewModel[];
    810  effectWorking: boolean | HttpErrorResponse;
    911}
     
    1113export const initialState: QuestionState = {
    1214  question: null,
     15  previewQuestionsLatest: [],
     16  previewQuestionsPopular: [],
    1317  effectWorking: false
    1418};
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/components/questioning-components.ts

    r81c2e6f r70e04f1  
    11import { QuestionPreviewGeneralComponent } from './question-preview-general/question-preview-general.component';
     2import { QuestioningGeneralComponent } from './questioning-general/questioning-general.component';
    23import { QuestionsPreviewGeneralComponent } from './questions-preview-general/questions-preview-general.component';
    34
    4 export const QUESTIONING_COMPONENTS: any[] = [QuestionPreviewGeneralComponent, QuestionsPreviewGeneralComponent];
     5export const QUESTIONING_COMPONENTS: any[] = [
     6  QuestionPreviewGeneralComponent,
     7  QuestionsPreviewGeneralComponent,
     8  QuestioningGeneralComponent
     9];
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/components/questions-preview-general/questions-preview-general.component.html

    r81c2e6f r70e04f1  
    1 <p>questions-preview-general works!</p>
     1<app-search-question></app-search-question>
     2<div class="margin-x-lg">
     3  <h1 class="mat-headline">{{ 'questions-preview' | translate }}</h1>
     4  <mat-button-toggle-group [formControl]="questionsSortByForm">
     5    <mat-button-toggle [value]="QuestionsSortBy.Latest">{{ 'question-sort-newest' | translate }}</mat-button-toggle>
     6    <mat-button-toggle [value]="QuestionsSortBy.Popular">{{ 'question-sort-popular' | translate }}</mat-button-toggle>
     7  </mat-button-toggle-group>
     8</div>
     9<app-preview-question-display
     10  *ngFor="let question of previewQuestions$ | async"
     11  [question]="question"
     12  (questionClicked)="goToQuestion(question.uid)"
     13></app-preview-question-display>
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/components/questions-preview-general/questions-preview-general.component.ts

    r81c2e6f r70e04f1  
    11import { Component, OnInit } from '@angular/core';
     2import { FormControl } from '@angular/forms';
     3import { Router } from '@angular/router';
    24import { CategoryFacadeService } from 'src/app/core/state/category-facade.service';
     5import { QuestionFacadeService } from 'src/app/core/state/question-facade.service';
     6import { CategoryStateViewModel, PreviewQuestionsOrderEnum } from 'src/app/shared-app/models';
    37
    48@Component({
     
    812})
    913export class QuestionsPreviewGeneralComponent implements OnInit {
    10   constructor(private categoriesFacade: CategoryFacadeService) {}
     14  categories?: CategoryStateViewModel[];
     15  previewQuestions$ = this.questionFacade.getPreviewQuestionsLatest();
     16  QuestionsSortBy = PreviewQuestionsOrderEnum;
     17  questionsSortByForm = new FormControl(PreviewQuestionsOrderEnum.Latest);
     18
     19  constructor(private categoriesFacade: CategoryFacadeService, private questionFacade: QuestionFacadeService, private router: Router) {}
    1120
    1221  ngOnInit(): void {
    1322    this.categoriesFacade.fetchCategories();
     23    this.questionFacade.fetchPreviewQuestions(PreviewQuestionsOrderEnum.Latest);
     24    this.questionsSortByForm.valueChanges.subscribe((value: PreviewQuestionsOrderEnum) => {
     25      this.questionFacade.fetchPreviewQuestions(value);
     26
     27      if (value === PreviewQuestionsOrderEnum.Latest) {
     28        this.previewQuestions$ = this.questionFacade.getPreviewQuestionsLatest();
     29      } else if (value === PreviewQuestionsOrderEnum.Popular) {
     30        this.previewQuestions$ = this.questionFacade.getPreviewQuestionsPopular();
     31      }
     32    });
     33  }
     34
     35  goToQuestion(uid: string): void {
     36    this.router.navigateByUrl(`questioning/${uid}`);
    1437  }
    1538}
  • src/Clients/Angular/finki-chattery/src/app/modules/questioning/questioning.routes.ts

    r81c2e6f r70e04f1  
    22import { Routes, RouterModule } from '@angular/router';
    33import { QuestionPreviewGeneralComponent } from './components/question-preview-general/question-preview-general.component';
     4import { QuestioningGeneralComponent } from './components/questioning-general/questioning-general.component';
    45import { QuestionsPreviewGeneralComponent } from './components/questions-preview-general/questions-preview-general.component';
    56
    67const routes: Routes = [
    78  {
    8     path: 'preview',
    9     pathMatch: 'full',
    10     component: QuestionsPreviewGeneralComponent
    11   },
    12   {
    13     path: ':questionUid',
    14     component: QuestionPreviewGeneralComponent
     9    component: QuestioningGeneralComponent,
     10    path: '',
     11    children: [
     12      {
     13        path: 'preview',
     14        pathMatch: 'full',
     15        component: QuestionsPreviewGeneralComponent
     16      },
     17      {
     18        path: ':questionUid',
     19        component: QuestionPreviewGeneralComponent
     20      }
     21    ]
    1522  }
    1623];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/components.ts

    r81c2e6f r70e04f1  
    44import { HeaderComponent } from './generic/header/header.component';
    55import { VoteComponent } from './generic/vote/vote.component';
     6import { PreviewQuestionDisplayComponent } from './question/preview-question-display/preview-question-display.component';
    67import { QuestionPreviewComponent } from './question/question-preview/question-preview.component';
     8import { SearchQuestionComponent } from './question/search-question/search-question.component';
    79import { StudentCardComponent } from './question/student-card/student-card.component';
    810
     
    1416  VoteComponent,
    1517  StudentCardComponent,
    16   HeaderComponent
     18  HeaderComponent,
     19  SearchQuestionComponent,
     20  PreviewQuestionDisplayComponent
    1721];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/index.ts

    r81c2e6f r70e04f1  
    33export * from './question-state-view-models.models';
    44export * from './category-state-view-models.models';
     5export * from './question-state-enums.models';
  • src/Clients/Angular/finki-chattery/src/app/shared-app/models/question-state-view-models.models.ts

    r81c2e6f r70e04f1  
    5454  constructor(public uid: string, public index: string, public imageUrl: string, public reputation: number) {}
    5555}
     56
     57export class PreviewQuestionViewModel {
     58  constructor(
     59    public uid: string,
     60    public title: string,
     61    public createdOn: moment.Moment,
     62    public views: number,
     63    public answersCount: number,
     64    public categories: PreviewQuestionCategoryViewModel[]
     65  ) {}
     66}
     67
     68export class PreviewQuestionCategoryViewModel {
     69  constructor(public uid: string, public text: string, public nameTranslated: string) {}
     70}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/pipes/moment-date.pipe.ts

    r81c2e6f r70e04f1  
    66})
    77export class MomentDatePipe implements PipeTransform {
    8   transform(value: moment.Moment, dateFormat: string): any {
     8  transform(value: moment.Moment | undefined | null, dateFormat: string): any {
    99    return moment(value).format(dateFormat);
    1010  }
  • src/Clients/Angular/finki-chattery/src/app/shared-material/shared-material.module.ts

    r81c2e6f r70e04f1  
    2323import { MatTooltipModule } from '@angular/material/tooltip';
    2424import { MatButtonToggleModule } from '@angular/material/button-toggle';
     25import { MatBadgeModule } from '@angular/material/badge';
    2526
    2627@NgModule({
     
    4748    MatChipsModule,
    4849    MatTooltipModule,
    49     MatButtonToggleModule
     50    MatButtonToggleModule,
     51    MatBadgeModule
    5052  ],
    5153  exports: [
     
    7072    MatChipsModule,
    7173    MatTooltipModule,
    72     MatButtonToggleModule
     74    MatButtonToggleModule,
     75    MatBadgeModule
    7376  ]
    7477})
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r81c2e6f r70e04f1  
    2828  "software-engineering": "Software engineering",
    2929  "visual-programming": "Visual programming",
    30   "operating-systems": "Operating systems"
     30  "operating-systems": "Operating systems",
     31  "internet-programming": "Internet programming",
     32  "object-oriented-programming": "Object oriented programming",
     33  "calculus": "Calculus",
     34  "discrete-mathematics": "Discrete mathematics",
     35  "web-programming": "Web programming",
     36  "advanced-programming": "Advanced programming",
     37  "questions-preview-find-question": "Search for a question",
     38  "questions-preview-find-question-categories": "Search in categories",
     39  "questions-search": "Search",
     40  "questions-preview": "Preview questions",
     41  "question-sort-newest": "Newest",
     42  "question-sort-popular": "Most popular",
     43  "questions-preview-question-subtitle": "Asked on: {{date}}",
     44  "questions-preview-question-answers": "Answers",
     45  "questions-preview-question-views": "Views"
    3146}
  • src/Clients/Angular/finki-chattery/src/styles.scss

    r81c2e6f r70e04f1  
    285285  z-index: 1100 !important;
    286286}
     287
     288.full-width {
     289  width: 100%;
     290}
Note: See TracChangeset for help on using the changeset viewer.