Changeset e071d30


Ignore:
Timestamp:
11/09/21 17:07:49 (3 years ago)
Author:
Стојков Марко <mst@…>
Branches:
dev
Children:
fcc3080
Parents:
53bebc0 (diff), 6165fd0 (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/delete-answer-and-answer-response into dev

Location:
src
Files:
8 added
18 edited

Legend:

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

    r53bebc0 re071d30  
    1616    return this.auth.selfUser?.student?.uid === answerResponse.student.uid;
    1717  }
     18
     19  currentUserCanDeleteAnswer(answer: AnswerQuestionStateViewModel): boolean {
     20    return this.auth.selfUser?.student?.uid === answer.student.uid;
     21  }
     22
     23  currentUserCanNotDeleteAnswerBecauseMarkedCorrectEvenThoughHeIsTheAuthor(answer: AnswerQuestionStateViewModel): boolean {
     24    return this.auth.selfUser?.student?.uid === answer.student.uid && answer.correctAnswer;
     25  }
     26
     27  currentUserCanDeleteAnswerResponse(answerResponse: AnswerResponseQuestionStateViewModel): boolean {
     28    return this.auth.selfUser?.student?.uid === answerResponse.student.uid;
     29  }
    1830}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-facade.service.ts

    r53bebc0 re071d30  
    1515import {
    1616  AnswerQuestion,
     17  DeleteAnswer,
     18  DeleteAnswerResponse,
    1719  EditAnswerQuestion,
    1820  EditAnswerResponse,
     
    101103  }
    102104
     105  public deleteAnswerResponse(answerUid: string, questionUid: string, answerResponseUid: string): void {
     106    this.dispatchEffect(new DeleteAnswerResponse(questionUid, answerUid, answerResponseUid));
     107  }
     108
     109  public deleteAnswer(answerUid: string, questionUid: string): void {
     110    this.dispatchEffect(new DeleteAnswer(questionUid, answerUid));
     111  }
     112
    103113  public fetchQuestion(questionUid: string): void {
    104114    this.dispatchEffect(new GetQuestionState(questionUid));
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.actions.ts

    r53bebc0 re071d30  
    3333  EditAnswerResponse = '[Question] EditAnswerResponse',
    3434  EditAnswerResponseSuccess = '[Question] EditAnswerResponse Success',
     35  DeleteAnswer = '[Question] DeleteAnswer',
     36  DeleteAnswerSuccess = '[Question] DeleteAnswer Success',
     37  DeleteAnswerResponse = '[Question] DeleteAnswerResponse',
     38  DeleteAnswerResponseSuccess = '[Question] DeleteAnswerResponse Success',
    3539  EffectStartedWorking = '[Question] Effect Started Working',
    3640  EffectFinishedWorking = '[Question] Effect Finished Working',
    3741  EffectFinishedWorkingError = '[Question] Effect Finished Working error'
     42}
     43
     44export class DeleteAnswer implements Action {
     45  readonly type = QuestionActionTypes.DeleteAnswer;
     46
     47  constructor(public questionUid: string, public answerUid: string) {}
     48}
     49
     50export class DeleteAnswerSuccess implements Action {
     51  readonly type = QuestionActionTypes.DeleteAnswerSuccess;
     52
     53  constructor(public payload: string) {}
     54}
     55
     56export class DeleteAnswerResponse implements Action {
     57  readonly type = QuestionActionTypes.DeleteAnswerResponse;
     58
     59  constructor(public questionUid: string, public answerUid: string, public answerResponseUid: string) {}
     60}
     61
     62export class DeleteAnswerResponseSuccess implements Action {
     63  readonly type = QuestionActionTypes.DeleteAnswerResponseSuccess;
     64
     65  constructor(public payload: string, public answerUid: string) {}
    3866}
    3967
     
    187215  | EditAnswerQuestionSuccess
    188216  | EditAnswerResponseSuccess
     217  | DeleteAnswerSuccess
     218  | DeleteAnswerResponseSuccess
    189219  | EffectStartedWorking
    190220  | EffectFinishedWorking
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.effects.ts

    r53bebc0 re071d30  
    1919  AnswerQuestion,
    2020  AnswerQuestionSuccess,
     21  DeleteAnswer,
     22  DeleteAnswerResponse,
     23  DeleteAnswerResponseSuccess,
     24  DeleteAnswerSuccess,
    2125  EditAnswerQuestion,
    2226  EditAnswerQuestionSuccess,
     
    236240    );
    237241  });
     242
     243  deleteResponseToAnswer$ = createEffect(() => {
     244    return this.actions$.pipe(
     245      ofType<DeleteAnswerResponse>(QuestionActionTypes.DeleteAnswerResponse),
     246      mergeMap((action) => {
     247        return this.api
     248          .delete<string>(`v1/questions/${action.questionUid}/answers/${action.answerUid}/answerresponses/${action.answerResponseUid}`)
     249          .pipe(
     250            tap((state) => this.notification.successNotification('success-delete-answer-response')),
     251            switchMap((state) => [new DeleteAnswerResponseSuccess(state, action.answerUid), new EffectFinishedWorking()]),
     252            catchError((err) => [new EffectFinishedWorkingError(err)])
     253          );
     254      })
     255    );
     256  });
     257
     258  deleteAnswer$ = createEffect(() => {
     259    return this.actions$.pipe(
     260      ofType<DeleteAnswer>(QuestionActionTypes.DeleteAnswer),
     261      mergeMap((action) => {
     262        return this.api.delete<string>(`v1/questions/${action.questionUid}/answers/${action.answerUid}`).pipe(
     263          tap((state) => this.notification.successNotification('success-delete-answer')),
     264          switchMap((state) => [new DeleteAnswerSuccess(state), new EffectFinishedWorking()]),
     265          catchError((err) => [new EffectFinishedWorkingError(err)])
     266        );
     267      })
     268    );
     269  });
    238270}
  • src/Clients/Angular/finki-chattery/src/app/core/state/question-state/question.reducers.ts

    r53bebc0 re071d30  
    162162      };
    163163    }
     164    case QuestionActionTypes.DeleteAnswerSuccess: {
     165      if (state.question) {
     166        return {
     167          ...state,
     168          question: {
     169            ...state.question,
     170            answers: state.question.answers.filter((x) => x.uid !== action.payload)
     171          }
     172        };
     173      }
     174
     175      return {
     176        ...state
     177      };
     178    }
     179    case QuestionActionTypes.DeleteAnswerResponseSuccess: {
     180      if (state.question) {
     181        return {
     182          ...state,
     183          question: {
     184            ...state.question,
     185            answers: state.question.answers.map((x) => {
     186              if (x.uid === action.answerUid) {
     187                return {
     188                  ...x,
     189                  answerResponses: x.answerResponses.filter((y) => y.uid !== action.payload)
     190                };
     191              }
     192
     193              return x;
     194            })
     195          }
     196        };
     197      }
     198
     199      return {
     200        ...state
     201      };
     202    }
    164203    case QuestionActionTypes.AnswerQuestionSuccess: {
    165204      if (state.question) {
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/components.ts

    r53bebc0 re071d30  
    11import { ButtonComponent } from './generic/button/button.component';
     2import { DeleteConfirmDialogComponent } from './generic/delete-confirm-dialog/delete-confirm-dialog.component';
    23import { FileUploadComponent } from './generic/file-upload/file-upload.component';
    34import { FormErrorComponent } from './generic/form-error/form-error.component';
     
    3233  AnswerQuestionComponent,
    3334  EditAnswerDialogComponent,
    34   EditAnswerResponseDialogComponent
     35  EditAnswerResponseDialogComponent,
     36  DeleteConfirmDialogComponent
    3537];
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.html

    r53bebc0 re071d30  
    4848              {{ answerResponse.createdOn | momentDate: 'LL' }}
    4949              <span
    50                 class="cursor text-bold"
     50                class="cursor text-bold padding-right-sm"
    5151                *ngIf="studentQuestion.currentUserCanEditAnswerResponse(answerResponse)"
    5252                (click)="editAnswerResponse(question.uid, answer.uid, answerResponse.uid, answerResponse.text)"
    5353                >{{ 'question-preview-edit-answer-response' | translate }}</span
     54              >
     55              <span
     56                class="cursor text-bold"
     57                *ngIf="studentQuestion.currentUserCanDeleteAnswerResponse(answerResponse)"
     58                (click)="deleteAnswerResponse(question.uid, answer.uid, answerResponse.uid)"
     59                >{{ 'delete-button' | translate }}</span
    5460              >
    5561              <hr />
     
    6874          >{{ 'question-preview-edit-answer' | translate }}</app-button
    6975        >
     76        <app-button
     77          *ngIf="studentQuestion.currentUserCanDeleteAnswer(answer)"
     78          [disabled]="studentQuestion.currentUserCanNotDeleteAnswerBecauseMarkedCorrectEvenThoughHeIsTheAuthor(answer)"
     79          matTooltip="{{ 'question-preview-can-not-delete-because-marked-correct' | translate }}"
     80          [matTooltipDisabled]="!studentQuestion.currentUserCanNotDeleteAnswerBecauseMarkedCorrectEvenThoughHeIsTheAuthor(answer)"
     81          (action)="deleteAnswer(question.uid, answer.uid)"
     82          [buttonType]="ButtonType.Basic"
     83          >{{ 'delete-button' | translate }}</app-button
     84        >
    7085      </mat-card-actions>
    7186    </mat-card>
  • src/Clients/Angular/finki-chattery/src/app/shared-app/components/question/question-preview/question-preview.component.ts

    r53bebc0 re071d30  
    5858    this.dialog.editResponseToAnswer(questionUid, answerUid, answerResponseUid, text);
    5959  }
     60
     61  deleteAnswer(questionUid: string, answerUid: string): void {
     62    this.dialog.confirmDelete().subscribe((canDelete) => {
     63      if (canDelete) {
     64        this.questionFacade.deleteAnswer(answerUid, questionUid);
     65      }
     66    });
     67  }
     68
     69  deleteAnswerResponse(questionUid: string, answerUid: string, answerResponseUid: string): void {
     70    this.dialog.confirmDelete().subscribe((canDelete) => {
     71      if (canDelete) {
     72        this.questionFacade.deleteAnswerResponse(answerUid, questionUid, answerResponseUid);
     73      }
     74    });
     75  }
    6076}
  • src/Clients/Angular/finki-chattery/src/app/shared-app/services/shared-dialog.service.ts

    r53bebc0 re071d30  
    22import { MatDialog, MatDialogRef } from '@angular/material/dialog';
    33import { Observable } from 'rxjs';
     4import { DeleteConfirmDialogComponent } from '../components/generic/delete-confirm-dialog/delete-confirm-dialog.component';
    45import { EditAnswerDialogComponent } from '../components/question/edit-answer-dialog/edit-answer-dialog.component';
    56// tslint:disable-next-line: max-line-length
     
    5455    return dialogRef.afterClosed();
    5556  }
     57
     58  public confirmDelete(title?: string): Observable<any> {
     59    let dialogRef: MatDialogRef<DeleteConfirmDialogComponent>;
     60    dialogRef = this.dialog.open(DeleteConfirmDialogComponent, {
     61      width: '650px',
     62      height: 'auto'
     63    });
     64
     65    if (title) {
     66      dialogRef.componentInstance.title = title;
     67    }
     68
     69    return dialogRef.afterClosed();
     70  }
    5671}
  • src/Clients/Angular/finki-chattery/src/assets/translations/en.json

    r53bebc0 re071d30  
    7373  "question-preview-edit-answer": "Edit answer",
    7474  "question-preview-edit-answer-response": "Edit response",
     75  "success-delete-answer-response": "Successfully deleted answer response",
     76  "success-delete-answer": "Successfully deleted answer",
     77  "delete-button": "Delete",
     78  "delete-basic-title": "Are you sure you want to delete this?",
     79  "question-preview-can-not-delete-because-marked-correct": "Even though you are the author of the answer, it can not be deleted because it's marked as correct by the user who asked the question",
    7580  "StudentDoesNotOwnQuestion": "You do not own this question",
    7681  "AnswerAlreadyUpvoted": "You have already upvoted this answer",
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/AnswerResponsesController.cs

    r53bebc0 re071d30  
    3939            return Ok(answerResponse.ToAnswerResponseQuestionStateResponse());
    4040        }
     41
     42        [HttpDelete("{answerResponseUid:Guid}")]
     43        [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Student)]
     44        public async Task<IActionResult> DeleteAnswerResponse([FromRoute] Guid questionUid, [FromRoute] Guid answerUid, [FromRoute] Guid answerResponseUid)
     45        {
     46            var uid = await MediatorService.SendAsync(new DeleteAnswerResponseCommand(questionUid, answerUid, answerResponseUid));
     47            return Ok(uid);
     48        }
    4149    }
    4250}
  • src/FinkiChattery/FinkiChattery.Api/Controllers/v1/AnswersController.cs

    r53bebc0 re071d30  
    4040        }
    4141
     42        [HttpDelete("{answerUid:Guid}")]
     43        [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Student)]
     44        public async Task<IActionResult> DeleteAnswer([FromRoute] Guid questionUid, [FromRoute] Guid answerUid)
     45        {
     46            var uid = await MediatorService.SendAsync(new DeleteAnswerCommand(questionUid, answerUid));
     47            return Ok(uid);
     48        }
     49
    4250        [HttpPut("{answerUid:Guid}/correct")]
    4351        [Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme, Policy = AuthenticationPolicy.Student)]
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/AnswerResponse/AnswerResponse.sql

    r53bebc0 re071d30  
    77    [CreatedOn] SMALLDATETIME    NOT NULL,
    88    CONSTRAINT [PK_AnswerResponse] PRIMARY KEY CLUSTERED ([Id] ASC),
    9     CONSTRAINT [FK_AnswerResponse_Answer_AnswerFk] FOREIGN KEY ([AnswerFk]) REFERENCES [dbo].[Answer] ([Id]),
     9    CONSTRAINT [FK_AnswerResponse_Answer_AnswerFk] FOREIGN KEY ([AnswerFk]) REFERENCES [dbo].[Answer] ([Id]) ON DELETE CASCADE,
    1010    CONSTRAINT [FK_AnswerResponse_Student_AnswerFk] FOREIGN KEY ([StudentFk]) REFERENCES [dbo].[Student] ([Id])
    1111);
  • src/FinkiChattery/FinkiChattery.Database/dbo/Tables/Vote/Vote.sql

    r53bebc0 re071d30  
    66    [VoteType] TINYINT           NOT NULL,
    77    CONSTRAINT [PK_Vote] PRIMARY KEY CLUSTERED ([Id] ASC),
    8     CONSTRAINT [FK_Vote_Answer_AnswerFk] FOREIGN KEY ([AnswerFk]) REFERENCES [dbo].[Answer] ([Id]),
     8    CONSTRAINT [FK_Vote_Answer_AnswerFk] FOREIGN KEY ([AnswerFk]) REFERENCES [dbo].[Answer] ([Id]) ON DELETE CASCADE,
    99    CONSTRAINT [FK_Vote_Student_StudentFk] FOREIGN KEY ([StudentFk]) REFERENCES [dbo].[Student] ([Id])
    1010);
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/AnswerResponseConfig.cs

    r53bebc0 re071d30  
    2727            builder.Property(x => x.CreatedOn).HasColumnName(@"CreatedOn").HasColumnType("smalldatetime").IsRequired();
    2828
    29             builder.HasOne(x => x.Answer).WithMany(x => x.AnswerResponses).HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Restrict);
     29            builder.HasOne(x => x.Answer).WithMany(x => x.AnswerResponses).HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Cascade);
    3030            builder.HasOne(x => x.Student).WithMany().HasForeignKey(x => x.StudentFk).OnDelete(DeleteBehavior.Restrict);
    3131        }
  • src/FinkiChattery/FinkiChattery.Persistence/Configurations/VoteConfig.cs

    r53bebc0 re071d30  
    2222
    2323            builder.HasOne(x => x.Student).WithMany().HasForeignKey(x => x.StudentFk).OnDelete(DeleteBehavior.Restrict);
    24             builder.HasOne(x => x.Answer).WithMany(x => x.Votes).HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Restrict);
     24            builder.HasOne(x => x.Answer).WithMany(x => x.Votes).HasForeignKey(x => x.AnswerFk).OnDelete(DeleteBehavior.Cascade);
    2525        }
    2626    }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/IRepository.cs

    r53bebc0 re071d30  
    1313        Task<T> GetByIdAsync(int id);
    1414
     15        void Delete(T entity);
     16
    1517        void Add(T entity);
    1618    }
  • src/FinkiChattery/FinkiChattery.Persistence/Repositories/Base/Repository.cs

    r53bebc0 re071d30  
    3838            return await All().FirstOrDefaultAsync(f => f.Uid == uid);
    3939        }
     40
     41        public void Delete(T entity)
     42        {
     43            DbSet.Remove(entity);
     44        }
    4045    }
    4146}
Note: See TracChangeset for help on using the changeset viewer.