From 7dd70008859f0547b7263a1c0df2e5975bb590e5 Mon Sep 17 00:00:00 2001 From: GRZ4NA Date: Sat, 27 May 2023 02:29:40 +0200 Subject: [PATCH 1/5] solutions system --- apps/backend/src/app.controller.ts | 39 +++++++++++++- apps/backend/src/app.module.ts | 2 + .../src/solutions/dto/create-solution.dto.ts | 13 +++++ .../src/solutions/entities/solution.entity.ts | 53 +++++++++++++++++++ .../backend/src/solutions/solutions.module.ts | 12 +++++ .../src/solutions/solutions.service.ts | 40 ++++++++++++++ 6 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 apps/backend/src/solutions/dto/create-solution.dto.ts create mode 100644 apps/backend/src/solutions/entities/solution.entity.ts create mode 100644 apps/backend/src/solutions/solutions.module.ts create mode 100644 apps/backend/src/solutions/solutions.service.ts diff --git a/apps/backend/src/app.controller.ts b/apps/backend/src/app.controller.ts index cd9ac38..b0b7c15 100644 --- a/apps/backend/src/app.controller.ts +++ b/apps/backend/src/app.controller.ts @@ -13,10 +13,14 @@ import { IssuesService } from './issues/issues.service'; import { IssueType } from './issues/entities/issue-type.enum'; import { CreateIssueDto } from './issues/dto/create-issue.dto'; import { CreateIssueCommentDto } from './issues/dto/create-issue-comment.dto'; +import { SolutionsService } from './solutions/solutions.service'; @Controller() export class AppController { - constructor(private readonly issues: IssuesService) {} + constructor( + private readonly issues: IssuesService, + private readonly solutions: SolutionsService, + ) {} @Get('/issues/problems') listProblems() { @@ -36,7 +40,14 @@ export class AppController { async getIssue(@Param('id', ParseUUIDPipe) id: string) { const data = await this.issues.findOne(id).then((i) => i.toDto()); const comments = await this.issues.getCommentsForIssue(id); - return { data, comments }; + const solution = + (await this.solutions.getForIssue(id))?.toList() ?? undefined; + return { data, comments, solution }; + } + + @Get('/issues/:id/solution') + async getSolution(@Param('id', ParseUUIDPipe) id: string) { + return this.solutions.getForIssue(id).then((i) => i.toDto()); } @Post('/issues') @@ -64,6 +75,18 @@ export class AppController { await this.issues.dislike(id); } + @Post('/issues/:id/solution/like') + @HttpCode(HttpStatus.ACCEPTED) + async addLikeToSolution(@Param('id', ParseUUIDPipe) id: string) { + await this.solutions.like(id); + } + + @Post('/issues/:id/solution/dislike') + @HttpCode(HttpStatus.ACCEPTED) + async addDislikeToSolution(@Param('id', ParseUUIDPipe) id: string) { + await this.solutions.dislike(id); + } + @Delete('/issues/:id/like') @HttpCode(HttpStatus.ACCEPTED) async removeLike(@Param('id', ParseUUIDPipe) id: string) { @@ -75,4 +98,16 @@ export class AppController { async removeDislike(@Param('id', ParseUUIDPipe) id: string) { await this.issues.dislike(id, true); } + + @Delete('/issues/:id/solution/like') + @HttpCode(HttpStatus.ACCEPTED) + async removeLikeToSolution(@Param('id', ParseUUIDPipe) id: string) { + await this.solutions.like(id, true); + } + + @Delete('/issues/:id/solution/dislike') + @HttpCode(HttpStatus.ACCEPTED) + async removeDislikeToSolution(@Param('id', ParseUUIDPipe) id: string) { + await this.solutions.dislike(id, true); + } } diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index fbe3c89..da2bb68 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { IssuesModule } from './issues/issues.module'; import { AppController } from './app.controller'; +import { SolutionsModule } from './solutions/solutions.module'; @Module({ controllers: [AppController], @@ -17,6 +18,7 @@ import { AppController } from './app.controller'; synchronize: process.env.NODE_ENV !== 'production', }), IssuesModule, + SolutionsModule, ], }) export class AppModule {} diff --git a/apps/backend/src/solutions/dto/create-solution.dto.ts b/apps/backend/src/solutions/dto/create-solution.dto.ts new file mode 100644 index 0000000..aef3ca7 --- /dev/null +++ b/apps/backend/src/solutions/dto/create-solution.dto.ts @@ -0,0 +1,13 @@ +import { IsString, MaxLength, MinLength } from 'class-validator'; + +export class CreateSolutionDto { + @IsString() + @MinLength(1) + @MaxLength(32) + title: string; + + @IsString() + @MinLength(1) + @MaxLength(255) + description: string; +} diff --git a/apps/backend/src/solutions/entities/solution.entity.ts b/apps/backend/src/solutions/entities/solution.entity.ts new file mode 100644 index 0000000..9f4eca8 --- /dev/null +++ b/apps/backend/src/solutions/entities/solution.entity.ts @@ -0,0 +1,53 @@ +import { Issue } from 'src/issues/entities/issue.entity'; +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; + +@Entity('solutions') +export class Solution { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + title: string; + + @Column() + username: string; + + @Column() + description: string; + + @CreateDateColumn() + createdAt: Date; + + @ManyToOne(() => Issue, { lazy: true, onDelete: 'CASCADE' }) + issue: Promise; + + @Column({ default: 0 }) + likes: number; + + @Column({ default: 0 }) + dislikes: number; + + async toDto() { + return { + ...this.toList(), + description: this.description, + issue: (await this.issue).toList(), + }; + } + + toList() { + return { + title: this.title, + username: this.username, + likes: this.likes, + dislikes: this.dislikes, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/apps/backend/src/solutions/solutions.module.ts b/apps/backend/src/solutions/solutions.module.ts new file mode 100644 index 0000000..b98060e --- /dev/null +++ b/apps/backend/src/solutions/solutions.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { SolutionsService } from './solutions.service'; +import { IssuesModule } from 'src/issues/issues.module'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Solution } from './entities/solution.entity'; + +@Module({ + imports: [IssuesModule, TypeOrmModule.forFeature([Solution])], + providers: [SolutionsService], + exports: [SolutionsService], +}) +export class SolutionsModule {} diff --git a/apps/backend/src/solutions/solutions.service.ts b/apps/backend/src/solutions/solutions.service.ts new file mode 100644 index 0000000..44b9288 --- /dev/null +++ b/apps/backend/src/solutions/solutions.service.ts @@ -0,0 +1,40 @@ +import { ConflictException, Injectable } from '@nestjs/common'; +import { CreateSolutionDto } from './dto/create-solution.dto'; +import { IssuesService } from 'src/issues/issues.service'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Solution } from './entities/solution.entity'; +import { Repository } from 'typeorm'; + +@Injectable() +export class SolutionsService { + constructor( + @InjectRepository(Solution) + private readonly repository: Repository, + private readonly issues: IssuesService, + ) {} + + async create(issueId: string, createSolutionDto: CreateSolutionDto) { + if ((await this.getForIssue(issueId)) !== null) + throw new ConflictException('This issue already has a solution'); + const solution = this.repository.create({ ...createSolutionDto }); + const issue = await this.issues.findOne(issueId); + solution.issue = Promise.resolve(issue); + return this.repository.save(solution); + } + + async getForIssue(issueId: string): Promise { + return this.repository.findOneBy({ issue: { id: issueId } }); + } + + async like(issueId: string, remove?: boolean) { + const ent = await this.getForIssue(issueId); + ent.likes = remove ? ent.likes - 1 : ent.likes + 1; + return this.repository.save(ent); + } + + async dislike(issueId: string, remove?: boolean) { + const ent = await this.getForIssue(issueId); + ent.dislikes = remove ? ent.dislikes - 1 : ent.dislikes + 1; + return this.repository.save(ent); + } +} From 1d8532bff392d9ce0a33a9ec22fdf56206f919b4 Mon Sep 17 00:00:00 2001 From: GRZ4NA Date: Sat, 27 May 2023 02:56:53 +0200 Subject: [PATCH 2/5] fix username assignment in comments for issue --- apps/backend/src/issues/issues.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/issues/issues.service.ts b/apps/backend/src/issues/issues.service.ts index 39066f5..f37b910 100644 --- a/apps/backend/src/issues/issues.service.ts +++ b/apps/backend/src/issues/issues.service.ts @@ -20,9 +20,9 @@ export class IssuesService { return this.repository.save(issue); } - async addComment(id: string, { content }: CreateIssueCommentDto) { + async addComment(id: string, dto: CreateIssueCommentDto) { const issue = await this.findOne(id); - const comment = this.comments.create({ content }); + const comment = this.comments.create(dto); comment.issue = Promise.resolve(issue); return this.comments.save(comment); } From 76787939c7567293ce365833f5cf053d04e801f3 Mon Sep 17 00:00:00 2001 From: GRZ4NA Date: Sat, 27 May 2023 03:33:23 +0200 Subject: [PATCH 3/5] solution comments + fixes --- apps/backend/src/app.controller.ts | 33 +++++++++++++++--- .../dto/create-solution-comment.dto.ts | 13 +++++++ .../entities/solution-comment.entity.ts | 34 +++++++++++++++++++ .../backend/src/solutions/solutions.module.ts | 6 +++- .../src/solutions/solutions.service.ts | 32 +++++++++++++++-- 5 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 apps/backend/src/solutions/dto/create-solution-comment.dto.ts create mode 100644 apps/backend/src/solutions/entities/solution-comment.entity.ts diff --git a/apps/backend/src/app.controller.ts b/apps/backend/src/app.controller.ts index b0b7c15..53ee2a8 100644 --- a/apps/backend/src/app.controller.ts +++ b/apps/backend/src/app.controller.ts @@ -14,6 +14,8 @@ import { IssueType } from './issues/entities/issue-type.enum'; import { CreateIssueDto } from './issues/dto/create-issue.dto'; import { CreateIssueCommentDto } from './issues/dto/create-issue-comment.dto'; import { SolutionsService } from './solutions/solutions.service'; +import { CreateSolutionDto } from './solutions/dto/create-solution.dto'; +import { CreateSolutionCommentDto } from './solutions/dto/create-solution-comment.dto'; @Controller() export class AppController { @@ -39,20 +41,27 @@ export class AppController { @Get('/issues/:id') async getIssue(@Param('id', ParseUUIDPipe) id: string) { const data = await this.issues.findOne(id).then((i) => i.toDto()); - const comments = await this.issues.getCommentsForIssue(id); + const comments = await this.issues + .getCommentsForIssue(id) + .then((c) => c.map((c) => c.toDto())); const solution = - (await this.solutions.getForIssue(id))?.toList() ?? undefined; + (await this.solutions.getForIssue(id).catch(() => undefined))?.toList() ?? + undefined; return { data, comments, solution }; } @Get('/issues/:id/solution') async getSolution(@Param('id', ParseUUIDPipe) id: string) { - return this.solutions.getForIssue(id).then((i) => i.toDto()); + const data = await this.solutions.getForIssue(id).then((i) => i.toDto()); + const comments = await this.solutions + .getComments(id) + .then((c) => c.map((c) => c.toDto())); + return { data, comments }; } @Post('/issues') createIssue(@Body() dto: CreateIssueDto) { - return this.issues.create(dto); + return this.issues.create(dto).then((i) => i.toDto()); } @Post('/issues/:id/comments') @@ -63,6 +72,22 @@ export class AppController { return this.issues.addComment(id, dto).then((c) => c.toDto()); } + @Post('/issues/:id/solution') + createSolution( + @Param('id', ParseUUIDPipe) id: string, + @Body() dto: CreateSolutionDto, + ) { + return this.solutions.create(id, dto).then((r) => r.toDto()); + } + + @Post('/issues/:id/solution/comments') + addSolutionComment( + @Body() dto: CreateSolutionCommentDto, + @Param('id', ParseUUIDPipe) id: string, + ) { + return this.solutions.addComment(id, dto).then((c) => c.toDto()); + } + @Post('/issues/:id/like') @HttpCode(HttpStatus.ACCEPTED) async addLike(@Param('id', ParseUUIDPipe) id: string) { diff --git a/apps/backend/src/solutions/dto/create-solution-comment.dto.ts b/apps/backend/src/solutions/dto/create-solution-comment.dto.ts new file mode 100644 index 0000000..a36e3c7 --- /dev/null +++ b/apps/backend/src/solutions/dto/create-solution-comment.dto.ts @@ -0,0 +1,13 @@ +import { IsString, MaxLength, MinLength } from 'class-validator'; + +export class CreateSolutionCommentDto { + @IsString() + @MinLength(3) + @MaxLength(16) + username: string; + + @IsString() + @MinLength(1) + @MaxLength(255) + content: string; +} diff --git a/apps/backend/src/solutions/entities/solution-comment.entity.ts b/apps/backend/src/solutions/entities/solution-comment.entity.ts new file mode 100644 index 0000000..ae0f1a1 --- /dev/null +++ b/apps/backend/src/solutions/entities/solution-comment.entity.ts @@ -0,0 +1,34 @@ +import { + Column, + CreateDateColumn, + Entity, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm'; +import { Solution } from './solution.entity'; + +@Entity('solution_comments') +export class SolutionComment { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + username: string; + + @Column() + content: string; + + @CreateDateColumn() + createdAt: Date; + + @ManyToOne(() => Solution, { lazy: true }) + solution: Promise; + + toDto() { + return { + username: this.username, + content: this.content, + createdAt: this.createdAt.toISOString(), + }; + } +} diff --git a/apps/backend/src/solutions/solutions.module.ts b/apps/backend/src/solutions/solutions.module.ts index b98060e..8a2f58d 100644 --- a/apps/backend/src/solutions/solutions.module.ts +++ b/apps/backend/src/solutions/solutions.module.ts @@ -3,9 +3,13 @@ import { SolutionsService } from './solutions.service'; import { IssuesModule } from 'src/issues/issues.module'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Solution } from './entities/solution.entity'; +import { SolutionComment } from './entities/solution-comment.entity'; @Module({ - imports: [IssuesModule, TypeOrmModule.forFeature([Solution])], + imports: [ + IssuesModule, + TypeOrmModule.forFeature([Solution, SolutionComment]), + ], providers: [SolutionsService], exports: [SolutionsService], }) diff --git a/apps/backend/src/solutions/solutions.service.ts b/apps/backend/src/solutions/solutions.service.ts index 44b9288..0369f11 100644 --- a/apps/backend/src/solutions/solutions.service.ts +++ b/apps/backend/src/solutions/solutions.service.ts @@ -1,15 +1,23 @@ -import { ConflictException, Injectable } from '@nestjs/common'; +import { + ConflictException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { CreateSolutionDto } from './dto/create-solution.dto'; import { IssuesService } from 'src/issues/issues.service'; import { InjectRepository } from '@nestjs/typeorm'; import { Solution } from './entities/solution.entity'; import { Repository } from 'typeorm'; +import { CreateSolutionCommentDto } from './dto/create-solution-comment.dto'; +import { SolutionComment } from './entities/solution-comment.entity'; @Injectable() export class SolutionsService { constructor( @InjectRepository(Solution) private readonly repository: Repository, + @InjectRepository(SolutionComment) + private readonly comments: Repository, private readonly issues: IssuesService, ) {} @@ -22,8 +30,15 @@ export class SolutionsService { return this.repository.save(solution); } - async getForIssue(issueId: string): Promise { - return this.repository.findOneBy({ issue: { id: issueId } }); + async getForIssue(issueId: string): Promise { + const ent = await this.repository.findOneBy({ issue: { id: issueId } }); + if (ent === null) { + throw new NotFoundException( + 'Solution for the given issue has not been found', + ); + } + + return ent; } async like(issueId: string, remove?: boolean) { @@ -37,4 +52,15 @@ export class SolutionsService { ent.dislikes = remove ? ent.dislikes - 1 : ent.dislikes + 1; return this.repository.save(ent); } + + async addComment(issueId: string, dto: CreateSolutionCommentDto) { + const ent = await this.getForIssue(issueId); + const comment = this.comments.create(dto); + comment.solution = Promise.resolve(ent); + return this.comments.save(comment); + } + + async getComments(id: string) { + return this.comments.findBy({ solution: { id } }); + } } From f9a5ddcde777ccaa5a0ac7714b5c0f49cfed57d1 Mon Sep 17 00:00:00 2001 From: GRZ4NA Date: Sat, 27 May 2023 03:54:22 +0200 Subject: [PATCH 4/5] update relation for issue <-> solution --- .../backend/src/solutions/entities/solution-comment.entity.ts | 2 +- apps/backend/src/solutions/entities/solution.entity.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/solutions/entities/solution-comment.entity.ts b/apps/backend/src/solutions/entities/solution-comment.entity.ts index ae0f1a1..6037e70 100644 --- a/apps/backend/src/solutions/entities/solution-comment.entity.ts +++ b/apps/backend/src/solutions/entities/solution-comment.entity.ts @@ -21,7 +21,7 @@ export class SolutionComment { @CreateDateColumn() createdAt: Date; - @ManyToOne(() => Solution, { lazy: true }) + @ManyToOne(() => Solution, { lazy: true, onDelete: 'CASCADE' }) solution: Promise; toDto() { diff --git a/apps/backend/src/solutions/entities/solution.entity.ts b/apps/backend/src/solutions/entities/solution.entity.ts index 9f4eca8..e617701 100644 --- a/apps/backend/src/solutions/entities/solution.entity.ts +++ b/apps/backend/src/solutions/entities/solution.entity.ts @@ -3,7 +3,7 @@ import { Column, CreateDateColumn, Entity, - ManyToOne, + OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; @@ -24,7 +24,7 @@ export class Solution { @CreateDateColumn() createdAt: Date; - @ManyToOne(() => Issue, { lazy: true, onDelete: 'CASCADE' }) + @OneToOne(() => Issue, { lazy: true, onDelete: 'CASCADE' }) issue: Promise; @Column({ default: 0 }) From 972b7f292cccbd022d10b14d8b04b7326a35c5d9 Mon Sep 17 00:00:00 2001 From: Just-Simon-Dev <39631698+Just-Simon-Dev@users.noreply.github.com> Date: Sat, 27 May 2023 04:00:16 +0200 Subject: [PATCH 5/5] =?UTF-8?q?some=20stuff=20idk=20what=20is=20it?= =?UTF-8?q?=F0=9F=98=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/app/models/solution.model.ts | 5 ++- .../solutions/solutions.component.html | 5 ++- .../modules/solutions/solutions.component.ts | 18 +++++++++-- .../src/app/services/solutions.service.ts | 32 +++++++++++++++++++ .../src/app/store/reducer/issue.reducer.ts | 6 ++++ .../app/store/selectors/issue.selectors.ts | 4 +++ 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 apps/frontend/src/app/services/solutions.service.ts diff --git a/apps/frontend/src/app/models/solution.model.ts b/apps/frontend/src/app/models/solution.model.ts index d7d9569..fed9526 100644 --- a/apps/frontend/src/app/models/solution.model.ts +++ b/apps/frontend/src/app/models/solution.model.ts @@ -1,5 +1,8 @@ import { CommentModel } from './comment.model'; export interface SolutionModel { - comments: CommentModel[]; + title: string, + description: string, + comments: CommentModel[], + createdAt: Date } diff --git a/apps/frontend/src/app/modules/solutions/solutions.component.html b/apps/frontend/src/app/modules/solutions/solutions.component.html index c418c67..90e3e1e 100644 --- a/apps/frontend/src/app/modules/solutions/solutions.component.html +++ b/apps/frontend/src/app/modules/solutions/solutions.component.html @@ -1 +1,4 @@ -

solutions works!

+
+ {{solution.title}} +
+gv gvghv \ No newline at end of file diff --git a/apps/frontend/src/app/modules/solutions/solutions.component.ts b/apps/frontend/src/app/modules/solutions/solutions.component.ts index 906aff9..c774346 100644 --- a/apps/frontend/src/app/modules/solutions/solutions.component.ts +++ b/apps/frontend/src/app/modules/solutions/solutions.component.ts @@ -1,10 +1,22 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { EMPTY, Observable } from 'rxjs'; +import { SolutionModel } from 'src/app/models/solution.model'; +import { receiveAllIssuesAction } from 'src/app/store/actions/issue.actions'; +import { selectAllSolutions } from 'src/app/store/selectors/issue.selectors'; +import { SolutionsModule } from './solutions.module'; @Component({ selector: 'app-solutions', templateUrl: './solutions.component.html', styleUrls: ['./solutions.component.scss'] }) -export class SolutionsComponent { - +export class SolutionsComponent implements OnInit { + solutions$: Observable = EMPTY + constructor(private store: Store){} + ngOnInit(): void { + this.store.dispatch(receiveAllIssuesAction()) + this.solutions$ = this.store.select(selectAllSolutions) + + } } diff --git a/apps/frontend/src/app/services/solutions.service.ts b/apps/frontend/src/app/services/solutions.service.ts new file mode 100644 index 0000000..1af2224 --- /dev/null +++ b/apps/frontend/src/app/services/solutions.service.ts @@ -0,0 +1,32 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { map } from 'rxjs'; +import { SolutionModel } from '../models/solution.model'; +import { CommentModel } from '../models/comment.model'; + +@Injectable({ + providedIn: 'root', +}) +export class IssueService { + constructor(private http: HttpClient) {} + + getSolutions(id: string) { + return this.http.get('/issue/'+id+'/solution').pipe( + map((solution) => + solution.map((solution) => ({ + title: solution.title, + description: solution.description, + comments: solution.comments, + createdAt: solution.createdAt + })) + ) + ); + } +} + +interface SolutionDto { + title: string, + description: string, + comments: CommentModel[], + createdAt: Date +} diff --git a/apps/frontend/src/app/store/reducer/issue.reducer.ts b/apps/frontend/src/app/store/reducer/issue.reducer.ts index d24f399..3daeb1b 100644 --- a/apps/frontend/src/app/store/reducer/issue.reducer.ts +++ b/apps/frontend/src/app/store/reducer/issue.reducer.ts @@ -24,6 +24,12 @@ const initialState: IssueState = { photos: [], status: 'new', type: 'problem', + solution: { + comments: [], + createdAt: new Date(), + description: "eoooo", + title: "title" + }, comments: [], }, { diff --git a/apps/frontend/src/app/store/selectors/issue.selectors.ts b/apps/frontend/src/app/store/selectors/issue.selectors.ts index 2a566fc..878c4ba 100644 --- a/apps/frontend/src/app/store/selectors/issue.selectors.ts +++ b/apps/frontend/src/app/store/selectors/issue.selectors.ts @@ -1,5 +1,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store'; import { IssueState } from '../state/issue.state'; +import { SolutionModel } from 'src/app/models/solution.model'; +import { IssueModel } from 'src/app/models/issue.model'; const selectIssueFeatureState = createFeatureSelector('issueState'); @@ -12,3 +14,5 @@ export const selectIssue = (id: string) => createSelector(selectAllIssues, (issues) => issues.find((issue) => issue.id === id) ); + + export const selectAllSolutions = createSelector(selectAllIssues, issues => issues.map(issue => issue.solution).filter(solution => solution !== undefined) as SolutionModel[]) \ No newline at end of file