Archived
comments
This commit is contained in:
1 parent
368cf265c5
commit
d035328d84
32 files changed
+426
-43
No files matched your search
@@ -31,6 +31,13 @@ const routes: Routes = [
|
||||
(m) => m.SolutionFormModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'issue/:id/solution',
|
||||
loadChildren: () =>
|
||||
import('./modules/solution/solution.module').then(
|
||||
(m) => m.SolutionModule
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -12,6 +12,7 @@ import { SidebarModule } from 'primeng/sidebar';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { IssueEffect } from './store/effect/issue.effect';
|
||||
import { SolutionEffect } from './store/effect/solution.effect';
|
||||
import { issueReducer } from './store/reducer/issue.reducer';
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
@@ -20,7 +21,7 @@ import { issueReducer } from './store/reducer/issue.reducer';
|
||||
AppRoutingModule,
|
||||
FontAwesomeModule,
|
||||
StoreModule.forRoot({ issueState: issueReducer }),
|
||||
EffectsModule.forRoot([IssueEffect]),
|
||||
EffectsModule.forRoot([IssueEffect, SolutionEffect]),
|
||||
HttpClientModule,
|
||||
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: !isDevMode() }),
|
||||
SidebarModule,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { CommentModel } from './comment.model';
|
||||
|
||||
export interface SolutionModel {
|
||||
id?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
comments: CommentModel[];
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CardModule } from 'primeng/card';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||
import { CommentFormComponent } from './components/comment-form/comment-form.component';
|
||||
import { CommentComponent } from './components/comment/comment.component';
|
||||
import { CommentsComponent } from './components/comments/comments.component';
|
||||
import { CommentService, commentTypeToken } from './services/comment.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CommentsComponent, CommentComponent, CommentFormComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
CardModule,
|
||||
InputTextModule,
|
||||
ButtonModule,
|
||||
InputTextareaModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
exports: [CommentsComponent],
|
||||
providers: [CommentService],
|
||||
})
|
||||
export class CommentModule {
|
||||
static forRoot(
|
||||
commentType: 'solution' | 'issues'
|
||||
): ModuleWithProviders<CommentModule> {
|
||||
return {
|
||||
ngModule: CommentModule,
|
||||
providers: [{ provide: commentTypeToken, useValue: commentType }],
|
||||
};
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<div class="comment-form">
|
||||
<form [formGroup]="formGroup">
|
||||
<p-card>
|
||||
<ng-template pTemplate="header">
|
||||
<div class="comment-header">
|
||||
<span class="p-float-label">
|
||||
<input
|
||||
pInputText
|
||||
id="username"
|
||||
type="text"
|
||||
formControlName="username"
|
||||
/>
|
||||
<label htmlFor="username">Nazwa Użytkownika</label>
|
||||
</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
<span class="p-float-label">
|
||||
<textarea
|
||||
id="content"
|
||||
rows="5"
|
||||
cols="40"
|
||||
pInputTextarea
|
||||
[autoResize]="true"
|
||||
formControlName="content"
|
||||
></textarea>
|
||||
<label htmlFor="content">Komentarz</label>
|
||||
</span>
|
||||
<ng-template pTemplate="footer">
|
||||
<div class="footer">
|
||||
<button
|
||||
pButton
|
||||
type="button"
|
||||
label="Dodaj"
|
||||
(click)="submit()"
|
||||
></button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-card>
|
||||
</form>
|
||||
</div>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
margin-right: 20px;
|
||||
padding: 28px 0 0 28px;
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommentFormComponent } from './comment-form.component';
|
||||
|
||||
describe('CommentFormComponent', () => {
|
||||
let component: CommentFormComponent;
|
||||
let fixture: ComponentFixture<CommentFormComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CommentFormComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(CommentFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { CommentService } from '../../services/comment.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-comment-form',
|
||||
templateUrl: './comment-form.component.html',
|
||||
styleUrls: ['./comment-form.component.scss'],
|
||||
})
|
||||
export class CommentFormComponent {
|
||||
@Input() issueId!: string;
|
||||
|
||||
constructor(private commentService: CommentService) {}
|
||||
formGroup = new FormGroup({
|
||||
content: new FormControl('', Validators.required),
|
||||
username: new FormControl('', Validators.required),
|
||||
});
|
||||
|
||||
submit() {
|
||||
const form = this.formGroup.getRawValue();
|
||||
this.commentService
|
||||
.addComment(this.issueId, form.username || '', form.content || '')
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<div class="comment">
|
||||
<p-card>
|
||||
<ng-template pTemplate="header">
|
||||
<p class="comment-header">{{ comment.userName }}</p>
|
||||
</ng-template>
|
||||
<p>{{ comment.content }}</p>
|
||||
<ng-template pTemplate="footer">
|
||||
<div class="date">
|
||||
{{ comment.createdAt | date : "dd.MM.yyyy, HH:mm" }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-card>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
.date {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
margin-right: 20px;
|
||||
padding: 20px 0 0 20px;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommentComponent } from './comment.component';
|
||||
|
||||
describe('CommentComponent', () => {
|
||||
let component: CommentComponent;
|
||||
let fixture: ComponentFixture<CommentComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CommentComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(CommentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CommentModel } from 'src/app/models/comment.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-comment',
|
||||
templateUrl: './comment.component.html',
|
||||
styleUrls: ['./comment.component.scss'],
|
||||
})
|
||||
export class CommentComponent {
|
||||
@Input() comment!: CommentModel;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<h2>Komentarze</h2>
|
||||
<div class="comments">
|
||||
<app-comment
|
||||
*ngFor="let comment of comments"
|
||||
[comment]="comment"
|
||||
></app-comment>
|
||||
<app-comment-form [issueId]="issueId"></app-comment-form>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
.comments {
|
||||
margin: 30px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommentsComponent } from './comments.component';
|
||||
|
||||
describe('CommentsComponent', () => {
|
||||
let component: CommentsComponent;
|
||||
let fixture: ComponentFixture<CommentsComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CommentsComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(CommentsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CommentModel } from 'src/app/models/comment.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-comments',
|
||||
templateUrl: './comments.component.html',
|
||||
styleUrls: ['./comments.component.scss'],
|
||||
})
|
||||
export class CommentsComponent {
|
||||
@Input() comments!: CommentModel[];
|
||||
@Input() issueId!: string;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable, InjectionToken } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { environments } from 'src/environments/environments';
|
||||
|
||||
export const commentTypeToken = new InjectionToken<'issues' | 'solution'>(
|
||||
'Comment Type'
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
export class CommentService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private store: Store,
|
||||
@Inject(commentTypeToken) private commentType: 'issues' | 'solution'
|
||||
) {}
|
||||
|
||||
addComment(issueId: string, username: string, content: string) {
|
||||
return this.http.post(
|
||||
environments.apiUrl +
|
||||
'/issues/' +
|
||||
issueId +
|
||||
(this.commentType === 'issues' ? '' : '/solution') +
|
||||
'/comments',
|
||||
{
|
||||
username,
|
||||
content,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,12 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IssueComponent } from './issue.component';
|
||||
|
||||
const routes: Routes = [{ path: ':id', component: IssueComponent }];
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: ':id',
|
||||
component: IssueComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
|
||||
@@ -21,25 +21,13 @@
|
||||
<p-button
|
||||
*ngIf="(issue$ | async)?.solution"
|
||||
label="Rozwiązanie"
|
||||
[routerLink]="['/', 'solution', (issue$ | async)?.solution?.id]"
|
||||
[routerLink]="['/', 'issue', (issue$ | async)?.id, 'solution']"
|
||||
></p-button>
|
||||
</div>
|
||||
</p-card>
|
||||
</div>
|
||||
<h2>Komentarze</h2>
|
||||
<div class="comments">
|
||||
<div class="comment" *ngFor="let comment of (issue$ | async)?.comments">
|
||||
<p-card>
|
||||
<ng-template pTemplate="header">
|
||||
<p class="comment-header">{{ comment.userName }}</p>
|
||||
</ng-template>
|
||||
<p>{{ comment.content }}</p>
|
||||
<ng-template pTemplate="footer">
|
||||
<div class="date">
|
||||
{{ comment.createdAt | date : "dd.MM.yyyy, HH:mm" }}
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-card>
|
||||
</div>
|
||||
<app-comments
|
||||
[comments]="(issue$ | async)?.comments || []"
|
||||
[issueId]="(issue$ | async)?.id || ''"
|
||||
></app-comments>
|
||||
</div>
|
||||
</div>
|
||||
@@ -12,13 +12,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.comments {
|
||||
margin: 30px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.main .content {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
@@ -30,21 +23,8 @@
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.date {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
margin-right: 20px;
|
||||
padding: 20px 0 0 20px;
|
||||
}
|
||||
|
||||
.issue {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
::ng-deep .p-card-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { NgModule } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CardModule } from 'primeng/card';
|
||||
import { TagModule } from 'primeng/tag';
|
||||
import { CommentModule } from '../comment/comment.module';
|
||||
import { IssueRoutingModule } from './issue-routing.module';
|
||||
import { IssueComponent } from './issue.component';
|
||||
|
||||
@@ -14,6 +15,7 @@ import { IssueComponent } from './issue.component';
|
||||
IssueRoutingModule,
|
||||
TagModule,
|
||||
ButtonModule,
|
||||
CommentModule.forRoot('issues'),
|
||||
],
|
||||
providers: [],
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Injectable } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { tap } from 'rxjs';
|
||||
import { IssueModel } from 'src/app/models/issue.model';
|
||||
import { receivedSolutionSuccessAction } from 'src/app/store/actions/solution.actions';
|
||||
import { environments } from 'src/environments/environments';
|
||||
|
||||
@Injectable({
|
||||
@@ -16,6 +17,20 @@ export class SolutionFormService {
|
||||
title,
|
||||
description,
|
||||
})
|
||||
.pipe(tap());
|
||||
.pipe(
|
||||
tap(() =>
|
||||
this.store.dispatch(
|
||||
receivedSolutionSuccessAction({
|
||||
issueId: issue.id,
|
||||
solution: {
|
||||
comments: [],
|
||||
createdAt: new Date(),
|
||||
description,
|
||||
title,
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { SolutionComponent } from './solution.component';
|
||||
|
||||
const routes: Routes = [{ path: '', component: SolutionComponent }];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class SolutionRoutingModule {}
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="solution">
|
||||
<div class="main">
|
||||
<p-card>
|
||||
<ng-template pTemplate="header">
|
||||
<div class="header">
|
||||
<h1>{{ (solution$ | async)?.title }}</h1>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div class="content">
|
||||
<p>{{ (solution$ | async)?.description }}</p>
|
||||
</div>
|
||||
</p-card>
|
||||
<app-comments
|
||||
[comments]="(solution$ | async)?.comments || []"
|
||||
[issueId]="issueId"
|
||||
></app-comments>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
.header {
|
||||
margin: 10px 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
h1 {
|
||||
flex: 7;
|
||||
}
|
||||
}
|
||||
|
||||
.main .content {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comments .content {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.solution {
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SolutionComponent } from './solution.component';
|
||||
|
||||
describe('SolutionComponent', () => {
|
||||
let component: SolutionComponent;
|
||||
let fixture: ComponentFixture<SolutionComponent>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SolutionComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(SolutionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { EMPTY, Observable } from 'rxjs';
|
||||
import { SolutionModel } from 'src/app/models/solution.model';
|
||||
import { selectSolution } from 'src/app/store/selectors/solution.selectors';
|
||||
|
||||
@Component({
|
||||
selector: 'app-solution',
|
||||
templateUrl: './solution.component.html',
|
||||
styleUrls: ['./solution.component.scss'],
|
||||
})
|
||||
export class SolutionComponent implements OnInit {
|
||||
solution$: Observable<SolutionModel | undefined> = EMPTY;
|
||||
issueId!: string;
|
||||
constructor(private store: Store, private route: ActivatedRoute) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params.subscribe((params) => {
|
||||
this.issueId = params['id'];
|
||||
this.solution$ = this.store.select(selectSolution(params['id']));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CardModule } from 'primeng/card';
|
||||
import { CommentModule } from '../comment/comment.module';
|
||||
import { SolutionRoutingModule } from './solution-routing.module';
|
||||
import { SolutionComponent } from './solution.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
CommentModule.forRoot('solution'),
|
||||
SolutionRoutingModule,
|
||||
CardModule,
|
||||
],
|
||||
declarations: [SolutionComponent],
|
||||
})
|
||||
export class SolutionModule {}
|
||||
@@ -1,4 +1,3 @@
|
||||
<div *ngFor="let solution of solutions$ | async">
|
||||
{{ solution.title }}
|
||||
</div>
|
||||
gv gvghv
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createSelector } from '@ngrx/store';
|
||||
import { selectIssue } from './issue.selectors';
|
||||
|
||||
export const selectSolution = (issueId: string) =>
|
||||
createSelector(selectIssue(issueId), (issue) => issue?.solution);
|
||||
@@ -1,4 +1,4 @@
|
||||
// Dev
|
||||
export const environments = {
|
||||
apiUrl: 'http://localhost',
|
||||
apiUrl: 'http://localhost/api',
|
||||
};
|
||||
@@ -16,3 +16,7 @@ h3,
|
||||
p {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.p-card-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
Reference in new issue
Block a user