mirror of https://github.com/abpframework/abp.git
23 changed files with 1051 additions and 31 deletions
@ -0,0 +1,72 @@ |
|||||
|
<abp-page [title]="'CmsKit::Comments' | abpLocalization"> |
||||
|
<div class="card mb-4"> |
||||
|
<div class="card-body"> |
||||
|
<form [formGroup]="filterForm" (ngSubmit)="onFilter()"> |
||||
|
<div class="row align-items-end"> |
||||
|
<div class="col-lg-4 col-md-12"> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationStartDate"> |
||||
|
{{ 'CmsKit::StartDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationStartDate" |
||||
|
formControlName="creationStartDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#startDatePicker="ngbDatepicker" |
||||
|
(click)="startDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationEndDate"> |
||||
|
{{ 'CmsKit::EndDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationEndDate" |
||||
|
formControlName="creationEndDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#endDatePicker="ngbDatepicker" |
||||
|
(click)="endDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-3 col-md-6"> |
||||
|
<abp-form-input |
||||
|
formControlName="author" |
||||
|
[label]="'CmsKit::Username' | abpLocalization" |
||||
|
type="text" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-3 col-md-6"> |
||||
|
<abp-form-input |
||||
|
formControlName="entityType" |
||||
|
[label]="'CmsKit::EntityType' | abpLocalization" |
||||
|
type="text" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-2 col-md-6"> |
||||
|
<abp-button class="w-100 mb-3" buttonType="Primary" type="submit"> |
||||
|
<i class="fa fa-search" aria-hidden="true"></i> |
||||
|
</abp-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="card"> |
||||
|
<abp-extensible-table [data]="data.items" [recordsTotal]="data.totalCount" [list]="list" /> |
||||
|
</div> |
||||
|
</abp-page> |
||||
@ -0,0 +1,122 @@ |
|||||
|
import { Component, OnInit, inject, LOCALE_ID } from '@angular/core'; |
||||
|
import { CommonModule, formatDate } from '@angular/common'; |
||||
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; |
||||
|
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; |
||||
|
import { ListService, LocalizationPipe, PagedResultDto } from '@abp/ng.core'; |
||||
|
import { ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; |
||||
|
import { PageComponent } from '@abp/ng.components/page'; |
||||
|
import { ButtonComponent, FormInputComponent } from '@abp/ng.theme.shared'; |
||||
|
import { |
||||
|
CommentAdminService, |
||||
|
CommentGetListInput, |
||||
|
CommentWithAuthorDto, |
||||
|
CommentApproveState, |
||||
|
} from '@abp/ng.cms-kit/proxy'; |
||||
|
import { eCmsKitAdminComponents } from '../../../enums'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-comment-approve', |
||||
|
templateUrl: './comment-approve.component.html', |
||||
|
providers: [ |
||||
|
ListService, |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: eCmsKitAdminComponents.CommentApprove, |
||||
|
}, |
||||
|
], |
||||
|
imports: [ |
||||
|
ReactiveFormsModule, |
||||
|
NgbDatepickerModule, |
||||
|
CommonModule, |
||||
|
PageComponent, |
||||
|
ButtonComponent, |
||||
|
FormInputComponent, |
||||
|
LocalizationPipe, |
||||
|
ExtensibleTableComponent, |
||||
|
], |
||||
|
}) |
||||
|
export class CommentApproveComponent implements OnInit { |
||||
|
data: PagedResultDto<CommentWithAuthorDto> = { items: [], totalCount: 0 }; |
||||
|
|
||||
|
public readonly list = inject(ListService<CommentGetListInput>); |
||||
|
private commentService = inject(CommentAdminService); |
||||
|
private fb = inject(FormBuilder); |
||||
|
private locale = inject(LOCALE_ID); |
||||
|
|
||||
|
filterForm!: FormGroup; |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.createFilterForm(); |
||||
|
this.hookToQuery(); |
||||
|
} |
||||
|
|
||||
|
private createFilterForm() { |
||||
|
this.filterForm = this.fb.group({ |
||||
|
creationStartDate: [null], |
||||
|
creationEndDate: [null], |
||||
|
author: [''], |
||||
|
entityType: [''], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onFilter() { |
||||
|
const formValue = this.filterForm.value; |
||||
|
const filters: Partial<CommentGetListInput> = { |
||||
|
author: formValue.author || undefined, |
||||
|
entityType: formValue.entityType || undefined, |
||||
|
commentApproveState: CommentApproveState.Waiting, |
||||
|
}; |
||||
|
|
||||
|
if (formValue.creationStartDate) { |
||||
|
filters.creationStartDate = this.formatDateForApi(formValue.creationStartDate); |
||||
|
} |
||||
|
|
||||
|
if (formValue.creationEndDate) { |
||||
|
filters.creationEndDate = this.formatDateForApi(formValue.creationEndDate); |
||||
|
} |
||||
|
|
||||
|
this.list.filter = JSON.stringify(filters); |
||||
|
this.list.get(); |
||||
|
} |
||||
|
|
||||
|
private formatDateForApi(date: any): string { |
||||
|
if (!date) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
if (typeof date === 'string') { |
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
if (date.year && date.month && date.day) { |
||||
|
const jsDate = new Date(date.year, date.month - 1, date.day); |
||||
|
return formatDate(jsDate, 'yyyy-MM-dd', this.locale); |
||||
|
} |
||||
|
|
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
private hookToQuery() { |
||||
|
this.list |
||||
|
.hookToQuery(query => { |
||||
|
let filters: Partial<CommentGetListInput> = { |
||||
|
commentApproveState: CommentApproveState.Waiting, |
||||
|
}; |
||||
|
if (this.list.filter) { |
||||
|
try { |
||||
|
filters = { ...filters, ...JSON.parse(this.list.filter) }; |
||||
|
} catch { |
||||
|
// Ignore parse errors, use default filters
|
||||
|
} |
||||
|
} |
||||
|
const input: CommentGetListInput = { |
||||
|
...query, |
||||
|
...filters, |
||||
|
}; |
||||
|
return this.commentService.getList(input); |
||||
|
}) |
||||
|
.subscribe(res => { |
||||
|
this.data = res; |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './comment-approve.component'; |
||||
@ -0,0 +1,147 @@ |
|||||
|
<abp-page [title]="'CmsKit::Comments' | abpLocalization"> |
||||
|
@if (comment) { |
||||
|
<div class="card mb-4"> |
||||
|
<div class="card-body"> |
||||
|
<table class="table"> |
||||
|
<tr> |
||||
|
<td width="10%"> |
||||
|
<b>{{ 'CmsKit::EntityType' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td>{{ comment.entityType }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b>{{ 'CmsKit::EntityId' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td>{{ comment.entityId }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b>{{ 'AbpIdentity::CreationTime' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td>{{ comment.creationTime | date }}</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b>{{ 'CmsKit::Username' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td>{{ comment.author?.name }}</td> |
||||
|
</tr> |
||||
|
@if (comment.repliedCommentId) { |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b>{{ 'CmsKit::ReplyTo' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td> |
||||
|
<a (click)="navigateToReply(comment.repliedCommentId!)" style="cursor: pointer"> |
||||
|
{{ comment.repliedCommentId }} |
||||
|
</a> |
||||
|
</td> |
||||
|
</tr> |
||||
|
} |
||||
|
<tr> |
||||
|
<td class="align-text-top"> |
||||
|
<b>{{ 'CmsKit::Text' | abpLocalization }}</b |
||||
|
>: |
||||
|
</td> |
||||
|
<td>{{ comment.text }}</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
|
|
||||
|
<div class="card mb-4"> |
||||
|
<div class="card-body"> |
||||
|
<form [formGroup]="filterForm" (ngSubmit)="onFilter()"> |
||||
|
<input type="hidden" [value]="commentId" /> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-4 col-md-12"> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationStartDate"> |
||||
|
{{ 'CmsKit::StartDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationStartDate" |
||||
|
formControlName="creationStartDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#startDatePicker="ngbDatepicker" |
||||
|
(click)="startDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationEndDate"> |
||||
|
{{ 'CmsKit::EndDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationEndDate" |
||||
|
formControlName="creationEndDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#endDatePicker="ngbDatepicker" |
||||
|
(click)="endDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-4 col-md-12"> |
||||
|
<abp-form-input |
||||
|
formControlName="author" |
||||
|
[label]="'CmsKit::Username' | abpLocalization" |
||||
|
type="text" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
@if (requireApprovement) { |
||||
|
<div class="col-lg-2 col-md-12"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="mb-1 form-label" for="commentApproveState"> |
||||
|
{{ 'CmsKit::CommentFilter:ApproveState' | abpLocalization }} |
||||
|
</label> |
||||
|
<select |
||||
|
id="commentApproveState" |
||||
|
formControlName="commentApproveState" |
||||
|
class="form-control" |
||||
|
> |
||||
|
@for (option of commentApproveStateOptions; track option.value) { |
||||
|
<option [value]="option.value"> |
||||
|
{{ 'CmsKit::CommentFilter:' + option.value | abpLocalization }} |
||||
|
</option> |
||||
|
} |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
|
|
||||
|
<div class="col-lg-2 col-md-12"> |
||||
|
<div class="d-grid gap-2"> |
||||
|
<abp-button class="mt-md-4" buttonType="Primary" type="submit"> |
||||
|
<i class="fa fa-search" aria-hidden="true"></i> |
||||
|
</abp-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<h3>{{ 'CmsKit::RepliesToThisComment' | abpLocalization }}</h3> |
||||
|
|
||||
|
<div class="card"> |
||||
|
<abp-extensible-table [data]="data.items" [recordsTotal]="data.totalCount" [list]="list" /> |
||||
|
</div> |
||||
|
</abp-page> |
||||
@ -0,0 +1,152 @@ |
|||||
|
import { Component, OnInit, inject, LOCALE_ID } from '@angular/core'; |
||||
|
import { CommonModule, DatePipe, formatDate } from '@angular/common'; |
||||
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; |
||||
|
import { ActivatedRoute, Router } from '@angular/router'; |
||||
|
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; |
||||
|
import { ListService, LocalizationPipe, PagedResultDto, ConfigStateService } from '@abp/ng.core'; |
||||
|
import { PageComponent } from '@abp/ng.components/page'; |
||||
|
import { ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; |
||||
|
import { ButtonComponent, FormInputComponent } from '@abp/ng.theme.shared'; |
||||
|
import { |
||||
|
CommentAdminService, |
||||
|
CommentGetListInput, |
||||
|
CommentWithAuthorDto, |
||||
|
CommentApproveState, |
||||
|
commentApproveStateOptions, |
||||
|
} from '@abp/ng.cms-kit/proxy'; |
||||
|
import { eCmsKitAdminComponents } from '../../../enums'; |
||||
|
import { CMS_KIT_COMMENTS_REQUIRE_APPROVEMENT } from '../comment-list'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-comment-details', |
||||
|
templateUrl: './comment-details.component.html', |
||||
|
providers: [ |
||||
|
ListService, |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: eCmsKitAdminComponents.CommentDetails, |
||||
|
}, |
||||
|
], |
||||
|
imports: [ |
||||
|
ExtensibleTableComponent, |
||||
|
PageComponent, |
||||
|
LocalizationPipe, |
||||
|
ReactiveFormsModule, |
||||
|
NgbDatepickerModule, |
||||
|
CommonModule, |
||||
|
DatePipe, |
||||
|
FormInputComponent, |
||||
|
ButtonComponent, |
||||
|
], |
||||
|
}) |
||||
|
export class CommentDetailsComponent implements OnInit { |
||||
|
comment: CommentWithAuthorDto | null = null; |
||||
|
data: PagedResultDto<CommentWithAuthorDto> = { items: [], totalCount: 0 }; |
||||
|
|
||||
|
public readonly list = inject(ListService<CommentGetListInput>); |
||||
|
private commentService = inject(CommentAdminService); |
||||
|
private route = inject(ActivatedRoute); |
||||
|
private router = inject(Router); |
||||
|
private fb = inject(FormBuilder); |
||||
|
private configState = inject(ConfigStateService); |
||||
|
private locale = inject(LOCALE_ID); |
||||
|
|
||||
|
filterForm!: FormGroup; |
||||
|
commentApproveStateOptions = commentApproveStateOptions; |
||||
|
requireApprovement = false; |
||||
|
commentId!: string; |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.requireApprovement = |
||||
|
this.configState.getSetting(CMS_KIT_COMMENTS_REQUIRE_APPROVEMENT) === 'true'; |
||||
|
this.route.params.subscribe(params => { |
||||
|
const id = params['id']; |
||||
|
if (id) { |
||||
|
this.commentId = id; |
||||
|
this.loadComment(id); |
||||
|
this.createFilterForm(); |
||||
|
this.hookToQuery(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private createFilterForm() { |
||||
|
this.filterForm = this.fb.group({ |
||||
|
creationStartDate: [null], |
||||
|
creationEndDate: [null], |
||||
|
author: [''], |
||||
|
commentApproveState: [CommentApproveState.All], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private loadComment(id: string) { |
||||
|
this.commentService.get(id).subscribe(comment => { |
||||
|
this.comment = comment; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onFilter() { |
||||
|
const formValue = this.filterForm.value; |
||||
|
const filters: Partial<CommentGetListInput> = { |
||||
|
author: formValue.author || undefined, |
||||
|
commentApproveState: formValue.commentApproveState, |
||||
|
repliedCommentId: this.commentId, |
||||
|
}; |
||||
|
|
||||
|
if (formValue.creationStartDate) { |
||||
|
filters.creationStartDate = this.formatDateForApi(formValue.creationStartDate); |
||||
|
} |
||||
|
|
||||
|
if (formValue.creationEndDate) { |
||||
|
filters.creationEndDate = this.formatDateForApi(formValue.creationEndDate); |
||||
|
} |
||||
|
|
||||
|
this.list.filter = JSON.stringify(filters); |
||||
|
this.list.get(); |
||||
|
} |
||||
|
|
||||
|
private formatDateForApi(date: any): string { |
||||
|
if (!date) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
if (typeof date === 'string') { |
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
if (date.year && date.month && date.day) { |
||||
|
const jsDate = new Date(date.year, date.month - 1, date.day); |
||||
|
return formatDate(jsDate, 'yyyy-MM-dd', this.locale); |
||||
|
} |
||||
|
|
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
private hookToQuery() { |
||||
|
this.list |
||||
|
.hookToQuery(query => { |
||||
|
let filters: Partial<CommentGetListInput> = { |
||||
|
repliedCommentId: this.commentId, |
||||
|
}; |
||||
|
if (this.list.filter) { |
||||
|
try { |
||||
|
filters = { ...filters, ...JSON.parse(this.list.filter) }; |
||||
|
} catch { |
||||
|
// Ignore parse errors, use default filters
|
||||
|
} |
||||
|
} |
||||
|
const input: CommentGetListInput = { |
||||
|
...query, |
||||
|
...filters, |
||||
|
}; |
||||
|
return this.commentService.getList(input); |
||||
|
}) |
||||
|
.subscribe(res => { |
||||
|
this.data = res; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
navigateToReply(id: string) { |
||||
|
this.router.navigate(['/cms/comments', id]); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './comment-details.component'; |
||||
@ -0,0 +1,91 @@ |
|||||
|
<abp-page [title]="'CmsKit::Comments' | abpLocalization"> |
||||
|
<div class="card mb-4"> |
||||
|
<div class="card-body"> |
||||
|
<form [formGroup]="filterForm" (ngSubmit)="onFilter()"> |
||||
|
<div class="row align-items-end"> |
||||
|
<div class="col-lg-4 col-md-12"> |
||||
|
<div class="row"> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationStartDate"> |
||||
|
{{ 'CmsKit::StartDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationStartDate" |
||||
|
formControlName="creationStartDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#startDatePicker="ngbDatepicker" |
||||
|
(click)="startDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="col-lg-6 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="form-label" for="creationEndDate"> |
||||
|
{{ 'CmsKit::EndDate' | abpLocalization }} |
||||
|
</label> |
||||
|
<input |
||||
|
id="creationEndDate" |
||||
|
formControlName="creationEndDate" |
||||
|
class="form-control" |
||||
|
ngbDatepicker |
||||
|
#endDatePicker="ngbDatepicker" |
||||
|
(click)="endDatePicker.toggle()" |
||||
|
readonly |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-2 col-md-6"> |
||||
|
<abp-form-input |
||||
|
formControlName="author" |
||||
|
[label]="'CmsKit::Username' | abpLocalization" |
||||
|
type="text" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div class="col-lg-2 col-md-6"> |
||||
|
<abp-form-input |
||||
|
formControlName="entityType" |
||||
|
[label]="'CmsKit::EntityType' | abpLocalization" |
||||
|
type="text" |
||||
|
/> |
||||
|
</div> |
||||
|
@if (requireApprovement) { |
||||
|
<div class="col-lg-2 col-md-6"> |
||||
|
<div class="mb-3"> |
||||
|
<label class="mb-1 form-label" for="commentApproveState"> |
||||
|
{{ 'CmsKit::CommentFilter:ApproveState' | abpLocalization }} |
||||
|
</label> |
||||
|
<select |
||||
|
id="commentApproveState" |
||||
|
formControlName="commentApproveState" |
||||
|
class="form-control" |
||||
|
> |
||||
|
@for (option of commentApproveStateOptions; track option.value) { |
||||
|
<option [value]="option.value"> |
||||
|
{{ 'CmsKit::CommentFilter:' + option.value | abpLocalization }} |
||||
|
</option> |
||||
|
} |
||||
|
</select> |
||||
|
</div> |
||||
|
</div> |
||||
|
} |
||||
|
<div class="col-lg-2 col-md-6"> |
||||
|
<abp-button class="w-100 mb-3" buttonType="Primary" type="submit"> |
||||
|
<i class="fa fa-search" aria-hidden="true"></i> |
||||
|
</abp-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="card"> |
||||
|
<abp-extensible-table [data]="data.items" [recordsTotal]="data.totalCount" [list]="list" /> |
||||
|
</div> |
||||
|
</abp-page> |
||||
@ -0,0 +1,128 @@ |
|||||
|
import { Component, OnInit, inject, LOCALE_ID } from '@angular/core'; |
||||
|
import { CommonModule, formatDate } from '@angular/common'; |
||||
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; |
||||
|
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; |
||||
|
import { ListService, PagedResultDto, ConfigStateService, LocalizationPipe } from '@abp/ng.core'; |
||||
|
import { ExtensibleModule, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; |
||||
|
import { PageModule } from '@abp/ng.components/page'; |
||||
|
import { ButtonComponent, FormInputComponent } from '@abp/ng.theme.shared'; |
||||
|
import { |
||||
|
CommentAdminService, |
||||
|
CommentGetListInput, |
||||
|
CommentWithAuthorDto, |
||||
|
CommentApproveState, |
||||
|
commentApproveStateOptions, |
||||
|
} from '@abp/ng.cms-kit/proxy'; |
||||
|
import { eCmsKitAdminComponents } from '../../../enums'; |
||||
|
|
||||
|
export const CMS_KIT_COMMENTS_REQUIRE_APPROVEMENT = 'CmsKit.Comments.RequireApprovement'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-comment-list', |
||||
|
standalone: true, |
||||
|
templateUrl: './comment-list.component.html', |
||||
|
providers: [ |
||||
|
ListService, |
||||
|
{ |
||||
|
provide: EXTENSIONS_IDENTIFIER, |
||||
|
useValue: eCmsKitAdminComponents.CommentList, |
||||
|
}, |
||||
|
], |
||||
|
imports: [ |
||||
|
ExtensibleModule, |
||||
|
PageModule, |
||||
|
ReactiveFormsModule, |
||||
|
NgbDatepickerModule, |
||||
|
CommonModule, |
||||
|
LocalizationPipe, |
||||
|
FormInputComponent, |
||||
|
ButtonComponent, |
||||
|
], |
||||
|
}) |
||||
|
export class CommentListComponent implements OnInit { |
||||
|
data: PagedResultDto<CommentWithAuthorDto> = { items: [], totalCount: 0 }; |
||||
|
|
||||
|
public readonly list = inject(ListService<CommentGetListInput>); |
||||
|
private commentService = inject(CommentAdminService); |
||||
|
private fb = inject(FormBuilder); |
||||
|
private configState = inject(ConfigStateService); |
||||
|
private locale = inject(LOCALE_ID); |
||||
|
|
||||
|
filterForm!: FormGroup; |
||||
|
commentApproveStateOptions = commentApproveStateOptions; |
||||
|
requireApprovement = false; |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.requireApprovement = |
||||
|
this.configState.getSetting(CMS_KIT_COMMENTS_REQUIRE_APPROVEMENT) === 'true'; |
||||
|
this.createFilterForm(); |
||||
|
this.hookToQuery(); |
||||
|
} |
||||
|
|
||||
|
private createFilterForm() { |
||||
|
this.filterForm = this.fb.group({ |
||||
|
creationStartDate: [null], |
||||
|
creationEndDate: [null], |
||||
|
author: [''], |
||||
|
entityType: [''], |
||||
|
commentApproveState: [CommentApproveState.All], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onFilter() { |
||||
|
const formValue = this.filterForm.value; |
||||
|
const filters: Partial<CommentGetListInput> = { |
||||
|
author: formValue.author || undefined, |
||||
|
entityType: formValue.entityType || undefined, |
||||
|
commentApproveState: formValue.commentApproveState, |
||||
|
}; |
||||
|
|
||||
|
if (formValue.creationStartDate) { |
||||
|
filters.creationStartDate = this.formatDateForApi(formValue.creationStartDate); |
||||
|
} |
||||
|
|
||||
|
if (formValue.creationEndDate) { |
||||
|
filters.creationEndDate = this.formatDateForApi(formValue.creationEndDate); |
||||
|
} |
||||
|
|
||||
|
this.list.filter = JSON.stringify(filters); |
||||
|
this.list.get(); |
||||
|
} |
||||
|
|
||||
|
private formatDateForApi(date: any): string { |
||||
|
if (!date) { |
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
if (typeof date === 'string') { |
||||
|
return date; |
||||
|
} |
||||
|
|
||||
|
if (date.year && date.month && date.day) { |
||||
|
const jsDate = new Date(date.year, date.month - 1, date.day); |
||||
|
return formatDate(jsDate, 'yyyy-MM-dd', this.locale); |
||||
|
} |
||||
|
|
||||
|
return ''; |
||||
|
} |
||||
|
|
||||
|
private hookToQuery() { |
||||
|
this.list |
||||
|
.hookToQuery(query => { |
||||
|
let filters: Partial<CommentGetListInput> = {}; |
||||
|
if (this.list.filter) { |
||||
|
try { |
||||
|
filters = JSON.parse(this.list.filter); |
||||
|
} catch { |
||||
|
// Ignore parse errors, use empty filters
|
||||
|
} |
||||
|
} |
||||
|
const input: CommentGetListInput = { |
||||
|
...query, |
||||
|
...filters, |
||||
|
}; |
||||
|
return this.commentService.getList(input); |
||||
|
}) |
||||
|
.subscribe(res => (this.data = res)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './comment-list.component'; |
||||
@ -0,0 +1,3 @@ |
|||||
|
export * from './comment-list'; |
||||
|
export * from './comment-approve'; |
||||
|
export * from './comment-details'; |
||||
@ -0,0 +1,62 @@ |
|||||
|
import { CommentWithAuthorDto } from '@abp/ng.cms-kit/proxy'; |
||||
|
import { EntityAction } from '@abp/ng.components/extensible'; |
||||
|
import { Router } from '@angular/router'; |
||||
|
import { CommentAdminService } from '@abp/ng.cms-kit/proxy'; |
||||
|
import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; |
||||
|
import { ConfigStateService, ListService } from '@abp/ng.core'; |
||||
|
|
||||
|
export const DEFAULT_COMMENT_ENTITY_ACTIONS = EntityAction.createMany<CommentWithAuthorDto>([ |
||||
|
{ |
||||
|
text: 'CmsKit::Details', |
||||
|
action: data => { |
||||
|
const router = data.getInjected(Router); |
||||
|
router.navigate(['/cms/comments', data.record.id]); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
text: 'CmsKit::Delete', |
||||
|
action: data => { |
||||
|
const commentService = data.getInjected(CommentAdminService); |
||||
|
const confirmation = data.getInjected(ConfirmationService); |
||||
|
const list = data.getInjected(ListService); |
||||
|
|
||||
|
confirmation |
||||
|
.warn('CmsKit::CommentDeletionConfirmationMessage', 'AbpUi::AreYouSure', { |
||||
|
yesText: 'AbpUi::Yes', |
||||
|
cancelText: 'AbpUi::Cancel', |
||||
|
}) |
||||
|
.subscribe(status => { |
||||
|
if (status === Confirmation.Status.confirm) { |
||||
|
commentService.delete(data.record.id!).subscribe(() => { |
||||
|
list.get(); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
permission: 'CmsKit.Comments.Delete', |
||||
|
}, |
||||
|
{ |
||||
|
// text: data => {
|
||||
|
// return data.record.isApproved ? 'CmsKit::Disapproved' : 'CmsKit::Approve';
|
||||
|
// },
|
||||
|
// TODO: Add a resolver for the text
|
||||
|
text: 'CmsKit::Approve', |
||||
|
action: data => { |
||||
|
const commentService = data.getInjected(CommentAdminService); |
||||
|
const list = data.getInjected(ListService); |
||||
|
const newApprovalStatus = !data.record.isApproved; |
||||
|
|
||||
|
commentService |
||||
|
.updateApprovalStatus(data.record.id!, { isApproved: newApprovalStatus }) |
||||
|
.subscribe(() => { |
||||
|
list.get(); |
||||
|
}); |
||||
|
}, |
||||
|
visible: data => { |
||||
|
const configState = data.getInjected(ConfigStateService); |
||||
|
const requireApprovement = |
||||
|
configState.getSetting('CmsKit.Comments.RequireApprovement') === 'true'; |
||||
|
return requireApprovement; |
||||
|
}, |
||||
|
}, |
||||
|
]); |
||||
@ -0,0 +1,78 @@ |
|||||
|
import { CommentWithAuthorDto } from '@abp/ng.cms-kit/proxy'; |
||||
|
import { EntityProp, ePropType } from '@abp/ng.components/extensible'; |
||||
|
import { ConfigStateService } from '@abp/ng.core'; |
||||
|
import { of } from 'rxjs'; |
||||
|
|
||||
|
export const DEFAULT_COMMENT_ENTITY_PROPS = EntityProp.createMany<CommentWithAuthorDto>([ |
||||
|
{ |
||||
|
type: ePropType.String, |
||||
|
name: 'author.userName', |
||||
|
displayName: 'CmsKit::Username', |
||||
|
sortable: false, |
||||
|
columnWidth: 150, |
||||
|
}, |
||||
|
{ |
||||
|
type: ePropType.String, |
||||
|
name: 'entityType', |
||||
|
displayName: 'CmsKit::EntityType', |
||||
|
sortable: false, |
||||
|
columnWidth: 200, |
||||
|
}, |
||||
|
{ |
||||
|
type: ePropType.String, |
||||
|
name: 'url', |
||||
|
displayName: 'CmsKit::URL', |
||||
|
sortable: false, |
||||
|
valueResolver: data => { |
||||
|
const url = data.record.url; |
||||
|
if (url) { |
||||
|
return of( |
||||
|
`<a href="${url}#comment-${data.record.id}" target="_blank"><i class="fa fa-location-arrow"></i></a>`, |
||||
|
); |
||||
|
} |
||||
|
return of(''); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
type: ePropType.String, |
||||
|
name: 'text', |
||||
|
displayName: 'CmsKit::Text', |
||||
|
sortable: false, |
||||
|
valueResolver: data => { |
||||
|
const text = data.record.text || ''; |
||||
|
const maxChars = 64; |
||||
|
if (text.length > maxChars) { |
||||
|
return of(text.substring(0, maxChars) + '...'); |
||||
|
} |
||||
|
return of(text); |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
type: ePropType.Boolean, |
||||
|
name: 'isApproved', |
||||
|
displayName: 'CmsKit::ApproveState', |
||||
|
sortable: false, |
||||
|
columnWidth: 100, |
||||
|
columnVisible: getInjected => { |
||||
|
const configState = getInjected(ConfigStateService); |
||||
|
return configState.getSetting('CmsKit.Comments.RequireApprovement') === 'true'; |
||||
|
}, |
||||
|
valueResolver: data => { |
||||
|
const isApproved = data.record.isApproved; |
||||
|
if (isApproved === null || isApproved === undefined) { |
||||
|
return of('<i class="fa-solid fa-hourglass-half text-muted"></i>'); |
||||
|
} else if (isApproved === true) { |
||||
|
return of('<i class="fa-solid fa-check text-success"></i>'); |
||||
|
} else { |
||||
|
return of('<i class="fa-solid fa-x text-danger"></i>'); |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
type: ePropType.Date, |
||||
|
name: 'creationTime', |
||||
|
displayName: 'AbpIdentity::CreationTime', |
||||
|
sortable: true, |
||||
|
columnWidth: 200, |
||||
|
}, |
||||
|
]); |
||||
@ -0,0 +1,2 @@ |
|||||
|
export * from './default-comment-entity-actions'; |
||||
|
export * from './default-comment-entity-props'; |
||||
@ -0,0 +1 @@ |
|||||
|
export * from './comments'; |
||||
@ -1,3 +1,29 @@ |
|||||
|
import { eCmsKitAdminComponents } from '../enums'; |
||||
|
import { |
||||
|
EntityActionContributorCallback, |
||||
|
EntityPropContributorCallback, |
||||
|
} from '@abp/ng.components/extensible'; |
||||
|
import { CommentWithAuthorDto } from '@abp/ng.cms-kit/proxy'; |
||||
|
|
||||
|
export type CmsKitAdminEntityActionContributors = Partial<{ |
||||
|
[eCmsKitAdminComponents.CommentList]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentApprove]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentDetails]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
}>; |
||||
|
|
||||
|
export type CmsKitAdminEntityPropContributors = Partial<{ |
||||
|
[eCmsKitAdminComponents.CommentList]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentApprove]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentDetails]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
}>; |
||||
|
|
||||
|
export type CmsKitAdminCreateFormPropContributors = Partial<{}>; |
||||
|
|
||||
|
export type CmsKitAdminEditFormPropContributors = Partial<{}>; |
||||
|
|
||||
export interface CmsKitAdminConfigOptions { |
export interface CmsKitAdminConfigOptions { |
||||
// Extension point contributors will be added here
|
entityActionContributors?: CmsKitAdminEntityActionContributors; |
||||
|
entityPropContributors?: CmsKitAdminEntityPropContributors; |
||||
|
createFormPropContributors?: CmsKitAdminCreateFormPropContributors; |
||||
|
editFormPropContributors?: CmsKitAdminEditFormPropContributors; |
||||
} |
} |
||||
|
|||||
@ -1,8 +1,6 @@ |
|||||
// export * from './components';
|
export * from './components'; |
||||
// export * from './defaults';
|
export * from './defaults'; |
||||
export * from './enums'; |
export * from './enums'; |
||||
// export * from './models';
|
|
||||
export * from './resolvers'; |
export * from './resolvers'; |
||||
// export * from './services';
|
|
||||
export * from './tokens'; |
export * from './tokens'; |
||||
export * from './cms-kit-admin.routes'; |
export * from './cms-kit-admin.routes'; |
||||
|
|||||
@ -1,8 +1,49 @@ |
|||||
|
import { |
||||
|
ExtensionsService, |
||||
|
getObjectExtensionEntitiesFromStore, |
||||
|
mapEntitiesToContributors, |
||||
|
mergeWithDefaultActions, |
||||
|
mergeWithDefaultProps, |
||||
|
} from '@abp/ng.components/extensible'; |
||||
|
import { inject, Injector } from '@angular/core'; |
||||
import { ResolveFn } from '@angular/router'; |
import { ResolveFn } from '@angular/router'; |
||||
|
import { map, tap } from 'rxjs'; |
||||
|
import { eCmsKitAdminComponents } from '../enums'; |
||||
|
import { |
||||
|
CMS_KIT_ADMIN_ENTITY_ACTION_CONTRIBUTORS, |
||||
|
CMS_KIT_ADMIN_ENTITY_PROP_CONTRIBUTORS, |
||||
|
DEFAULT_CMS_KIT_ADMIN_ENTITY_ACTIONS, |
||||
|
DEFAULT_CMS_KIT_ADMIN_ENTITY_PROPS, |
||||
|
} from '../tokens'; |
||||
|
|
||||
// Resolvers will be defined here
|
export const cmsKitAdminExtensionsResolver: ResolveFn<any> = () => { |
||||
// Example:
|
const injector = inject(Injector); |
||||
// export const cmsKitAdminResolver: ResolveFn<any> = (route, state) => {
|
const extensions = inject(ExtensionsService); |
||||
// // Resolver implementation
|
|
||||
// return null;
|
const config = { optional: true }; |
||||
// };
|
|
||||
|
const actionContributors = inject(CMS_KIT_ADMIN_ENTITY_ACTION_CONTRIBUTORS, config) || {}; |
||||
|
const propContributors = inject(CMS_KIT_ADMIN_ENTITY_PROP_CONTRIBUTORS, config) || {}; |
||||
|
|
||||
|
return getObjectExtensionEntitiesFromStore(injector, 'CmsKit').pipe( |
||||
|
map(entities => ({ |
||||
|
[eCmsKitAdminComponents.CommentList]: entities.Comment, |
||||
|
[eCmsKitAdminComponents.CommentApprove]: entities.Comment, |
||||
|
[eCmsKitAdminComponents.CommentDetails]: entities.Comment, |
||||
|
})), |
||||
|
mapEntitiesToContributors(injector, 'CmsKit'), |
||||
|
tap(objectExtensionContributors => { |
||||
|
mergeWithDefaultActions( |
||||
|
extensions.entityActions, |
||||
|
DEFAULT_CMS_KIT_ADMIN_ENTITY_ACTIONS, |
||||
|
actionContributors, |
||||
|
); |
||||
|
mergeWithDefaultProps( |
||||
|
extensions.entityProps, |
||||
|
DEFAULT_CMS_KIT_ADMIN_ENTITY_PROPS, |
||||
|
objectExtensionContributors.prop, |
||||
|
propContributors, |
||||
|
); |
||||
|
}), |
||||
|
); |
||||
|
}; |
||||
|
|||||
@ -1,4 +1,41 @@ |
|||||
|
import { CommentWithAuthorDto } from '@abp/ng.cms-kit/proxy'; |
||||
|
import { |
||||
|
EntityActionContributorCallback, |
||||
|
EntityPropContributorCallback, |
||||
|
} from '@abp/ng.components/extensible'; |
||||
import { InjectionToken } from '@angular/core'; |
import { InjectionToken } from '@angular/core'; |
||||
|
import { DEFAULT_COMMENT_ENTITY_ACTIONS } from '../defaults/comments/default-comment-entity-actions'; |
||||
|
import { DEFAULT_COMMENT_ENTITY_PROPS } from '../defaults/comments/default-comment-entity-props'; |
||||
|
import { eCmsKitAdminComponents } from '../enums'; |
||||
|
|
||||
// Extension tokens will be defined here
|
export const DEFAULT_CMS_KIT_ADMIN_ENTITY_ACTIONS = { |
||||
export const EXTENSIONS_IDENTIFIER = new InjectionToken<string>('EXTENSIONS_IDENTIFIER'); |
[eCmsKitAdminComponents.CommentList]: DEFAULT_COMMENT_ENTITY_ACTIONS, |
||||
|
[eCmsKitAdminComponents.CommentApprove]: DEFAULT_COMMENT_ENTITY_ACTIONS, |
||||
|
[eCmsKitAdminComponents.CommentDetails]: DEFAULT_COMMENT_ENTITY_ACTIONS, |
||||
|
}; |
||||
|
|
||||
|
export const DEFAULT_CMS_KIT_ADMIN_ENTITY_PROPS = { |
||||
|
[eCmsKitAdminComponents.CommentList]: DEFAULT_COMMENT_ENTITY_PROPS, |
||||
|
[eCmsKitAdminComponents.CommentApprove]: DEFAULT_COMMENT_ENTITY_PROPS, |
||||
|
[eCmsKitAdminComponents.CommentDetails]: DEFAULT_COMMENT_ENTITY_PROPS, |
||||
|
}; |
||||
|
|
||||
|
export const CMS_KIT_ADMIN_ENTITY_ACTION_CONTRIBUTORS = |
||||
|
new InjectionToken<EntityActionContributors>('CMS_KIT_ADMIN_ENTITY_ACTION_CONTRIBUTORS'); |
||||
|
|
||||
|
export const CMS_KIT_ADMIN_ENTITY_PROP_CONTRIBUTORS = new InjectionToken<EntityPropContributors>( |
||||
|
'CMS_KIT_ADMIN_ENTITY_PROP_CONTRIBUTORS', |
||||
|
); |
||||
|
|
||||
|
// Fix for TS4023 -> https://github.com/microsoft/TypeScript/issues/9944#issuecomment-254693497
|
||||
|
type EntityActionContributors = Partial<{ |
||||
|
[eCmsKitAdminComponents.CommentList]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentApprove]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentDetails]: EntityActionContributorCallback<CommentWithAuthorDto>[]; |
||||
|
}>; |
||||
|
|
||||
|
type EntityPropContributors = Partial<{ |
||||
|
[eCmsKitAdminComponents.CommentList]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentApprove]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
[eCmsKitAdminComponents.CommentDetails]: EntityPropContributorCallback<CommentWithAuthorDto>[]; |
||||
|
}>; |
||||
|
|||||
Loading…
Reference in new issue