Changeset 8b6791f for src


Ignore:
Timestamp:
10/22/21 21:47:17 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
466d1ac, 5ad5988
Parents:
81c2e6f (diff), 70e04f1 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merged feature/preview-questions-and-search into dev

Location:
src
Files:
18 added
29 edited
1 moved

Legend:

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

    r81c2e6f r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    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 r8b6791f  
    285285  z-index: 1100 !important;
    286286}
     287
     288.full-width {
     289  width: 100%;
     290}
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/CategoryMapper.cs

    r81c2e6f r8b6791f  
    1 using FinkiChattery.Contracts.Questioning.GetCategories;
     1using FinkiChattery.Contracts.Questioning;
    22using FinkiChattery.Persistence.Models;
    33using System.Collections.Generic;
     
    88    public static class CategoryMapper
    99    {
    10         public static List<CategoryDto> ToCategoryDtos(this IEnumerable<Category> categories)
     10        public static List<CategoryResponse> ToCategoryDtos(this IEnumerable<Category> categories)
    1111        {
    12             var categoryDtos = new List<CategoryDto>();
     12            var categoryDtos = new List<CategoryResponse>();
    1313
    1414            if (categories.Any())
    1515            {
    16                 categoryDtos = categories.Select(x => new CategoryDto(x.Uid, x.Name)).ToList();
     16                categoryDtos = categories.Select(x => new CategoryResponse(x.Uid, x.Name)).ToList();
    1717            }
    1818
  • src/FinkiChattery/FinkiChattery.Api/ApplicationServices/Questioning/Mapper/QuestionMapper.cs

    r81c2e6f r8b6791f  
    11#nullable enable
    22
    3 using FinkiChattery.Contracts.Questioning.GetQuestionState;
     3using FinkiChattery.Contracts.Questioning;
    44using FinkiChattery.Persistence.Repositories.Contracts;
    55using System.Collections.Generic;
     
    1010    public static class QuestionMapper
    1111    {
     12        public static List<PreviewQuestionResponse> ToPreviewQuestionsResponse(this IEnumerable<QuestionPreviewDto> questions)
     13        {
     14            var questionsResponse = new List<PreviewQuestionResponse>();
     15
     16            if (questions.Any())
     17            {
     18                questionsResponse = questions.Select(x =>
     19                {
     20                    var questionCategoriesResponse = new List<PreviewQuestionCategoryResponse>();
     21
     22                    if (x.Categories.Any())
     23                    {
     24                        questionCategoriesResponse = x.Categories.Select(y => new PreviewQuestionCategoryResponse(y.Id, y.Uid, y.Text)).ToList();
     25                    }
     26
     27                    return new PreviewQuestionResponse(x.Id, x.Uid, x.Title, x.Views, x.AnswersCount, x.CreatedOn, questionCategoriesResponse);
     28                }).ToList();
     29            }
     30
     31            return questionsResponse;
     32        }
     33
    1234        public static QuestionStateResponse ToQuestionStateResponse(this QuestionStateDto questionState)
    1335        {
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/QuestionsController.cs

    r81c2e6f r8b6791f  
    4040            return Ok(questionDto.ToQuestionStateResponse());
    4141        }
     42
     43        [HttpGet("preview")]
     44        [Authorize]
     45        public async Task<IActionResult> PreviewQuestions([FromQuery] GetPreviewQuestionsOrderEnum order)
     46        {
     47            var questions = await MediatorService.SendQueryAsync(new GetPreviewQuestionsQuery(order));
     48            return Ok(questions.ToPreviewQuestionsResponse());
     49        }
    4250    }
    4351}
  • src/FinkiChattery/FinkiChattery.Contracts/Questioning/GetCategories/CategoryResponse.cs

    r81c2e6f r8b6791f  
    11using System;
    22
    3 namespace FinkiChattery.Contracts.Questioning.GetCategories
     3namespace FinkiChattery.Contracts.Questioning
    44{
    5     public class CategoryDto
     5    public class CategoryResponse
    66    {
    7         public CategoryDto(Guid uid, string name)
     7        public CategoryResponse(Guid uid, string name)
    88        {
    99            Uid = uid;
  • src/FinkiChattery/FinkiChattery.Contracts/Questioning/GetQuestionState/QuestionStateResponse.cs

    r81c2e6f r8b6791f  
    55using System.Text.Json.Serialization;
    66
    7 namespace FinkiChattery.Contracts.Questioning.GetQuestionState
     7namespace FinkiChattery.Contracts.Questioning
    88{
    99    public class QuestionStateResponse
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Question/Question.Debug.Seed.sql

    r81c2e6f r8b6791f  
    99        VALUES
    1010            (1, N'aee193c3-9d36-4ed8-81b2-15eb4ff305f1', N'Question 1', 'Question 1 text', 1, GETUTCDATE(), NULL, 0,
    11              GETUTCDATE()
     11             GETUTCDATE(), 2
    1212            ),
    1313            (2, N'bee193c3-9d36-4ed8-81b2-15eb4ff305f1', N'Question 2', 'Question 2 text', 1, GETUTCDATE(), NULL, 0,
    14              GETUTCDATE()
     14             GETUTCDATE(), 2
    1515            )
    16     ) AS temp ([ID], [Uid], [Title], [Text], [StudentFk], [CreatedOn], [TeamFk], [Views], [LastActiveOn])
     16    ) AS temp ([ID], [Uid], [Title], [Text], [StudentFk], [CreatedOn], [TeamFk], [Views], [LastActiveOn], [AnswersCount])
    1717    ) AS S
    1818    ON T.[ID] = S.[ID]
     
    2525                   T.[TeamFk] = S.[TeamFk],
    2626                   T.[Views] = S.[Views],
    27                    T.[LastActiveOn] = S.[LastActiveOn]
     27                   T.[LastActiveOn] = S.[LastActiveOn],
     28                   T.[AnswersCount] = S.[AnswersCount]
    2829    WHEN NOT MATCHED THEN
    2930        INSERT
     
    3738            [TeamFk],
    3839            [Views],
    39             [LastActiveOn]
     40            [LastActiveOn],
     41            [AnswersCount]
    4042        )
    4143        VALUES
    42         (S.[ID], S.[Uid], S.[Title], S.[Text], S.[StudentFk], S.[CreatedOn], S.[TeamFk], S.[Views], S.[LastActiveOn]);
     44        (S.[ID], S.[Uid], S.[Title], S.[Text], S.[StudentFk], S.[CreatedOn], S.[TeamFk], S.[Views], S.[LastActiveOn], S.[AnswersCount]);
    4345    SET IDENTITY_INSERT [dbo].[Question] OFF
    4446END
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Question/Question.sql

    r81c2e6f r8b6791f  
    1010    [LastActiveOn] SMALLDATETIME    NOT NULL,
    1111    [Search]       AS ([Title] + ' ' + [Text]),
     12    [AnswersCount] BIGINT           NOT NULL DEFAULT 0,
    1213    CONSTRAINT [PK_Question] PRIMARY KEY CLUSTERED ([Id] ASC),
    1314    CONSTRAINT [FK_Question_Student_StudentFk] FOREIGN KEY ([StudentFk]) REFERENCES [dbo].[Student] ([Id]),
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/QuestionConfig.cs

    r81c2e6f r8b6791f  
    2525            builder.Property(x => x.LastActiveOn).HasColumnName(@"LastActiveOn").HasColumnType("smalldatetime").IsRequired();
    2626            builder.Property(x => x.Search).HasColumnType(@"Search").HasColumnType("nvarchar").HasMaxLength(4000).IsRequired();
     27            builder.Property(x => x.AnswersCount).HasColumnType(@"AnswersCount").HasColumnType("bigint").IsRequired().HasDefaultValue(0);
    2728
    2829            builder.HasOne(x => x.Student).WithMany(x => x.Questions).HasForeignKey(x => x.StudentFk).OnDelete(DeleteBehavior.NoAction);
  • src/FinkiChattery/FinkiChattery.Persistence/Models/Question.cs

    r81c2e6f r8b6791f  
    3434        public string Search { get; set; }
    3535
     36        public long AnswersCount { get; set; }
     37
    3638        public virtual ICollection<Answer> Answers { get; set; }
    3739
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Contracts/IQuestionRepo.cs

    r81c2e6f r8b6791f  
    22using FinkiChattery.Persistence.Repositories.Contracts;
    33using System;
     4using System.Collections.Generic;
    45using System.Threading.Tasks;
    56
     
    910    {
    1011        Task<QuestionStateDto> GetQuestionState(Guid questionUid);
     12
     13        Task<List<QuestionPreviewDto>> GetPreviewQuestionsLatest();
     14     
     15        Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular();
    1116    }
    1217}
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Implementations/QuestionRepo.cs

    r81c2e6f r8b6791f  
    44using Microsoft.EntityFrameworkCore;
    55using System;
     6using System.Collections.Generic;
    67using System.Linq;
     8using System.Linq.Expressions;
    79using System.Threading.Tasks;
    810
     
    1315        public QuestionRepo(ApplicationDbContext dbContext) : base(dbContext)
    1416        {
     17        }
     18
     19        public async Task<List<QuestionPreviewDto>> GetPreviewQuestionsLatest()
     20        {
     21            Expression<Func<Question, DateTime>> orderBy = x => x.CreatedOn;
     22            return await GetPreviewQuestions(orderBy);
     23        }
     24
     25        public async Task<List<QuestionPreviewDto>> GetPreviewQuestionsPopular()
     26        {
     27            Expression<Func<Question, long>> orderBy = x => x.Views;
     28            return await GetPreviewQuestions(orderBy);
     29        }
     30
     31        private async Task<List<QuestionPreviewDto>> GetPreviewQuestions<T>(Expression<Func<Question, T>> orderBy)
     32        {
     33            return await DbSet
     34                .AsNoTracking()
     35                .Include(x => x.QuestionCategories).ThenInclude(x => x.Category)
     36                .OrderByDescending(orderBy)
     37                .Select(x => new QuestionPreviewDto(x.Id,
     38                                                    x.Uid,
     39                                                    x.Title,
     40                                                    x.Views,
     41                                                    x.AnswersCount,
     42                                                    x.CreatedOn,
     43                                                    x.QuestionCategories.Select(y => new QuestionPreviewCategoryDto(y.Id, y.Uid, y.Category.Name))))
     44                .Skip(0).Take(30)
     45                .ToListAsync();
    1546        }
    1647
Note: See TracChangeset for help on using the changeset viewer.