diff --git a/README.md b/README.md index 37196aed..3af4a186 100644 --- a/README.md +++ b/README.md @@ -185,4 +185,7 @@ _none_ ### Frontend -**Safari is NOT supported** +* Task duration greater than `9999999` break things. Do not use such long tasks. +* Safari is NOT supported + + diff --git a/backend/src/main/kotlin/dev/dres/DRES.kt b/backend/src/main/kotlin/dev/dres/DRES.kt index 8db32b7b..debb31b0 100644 --- a/backend/src/main/kotlin/dev/dres/DRES.kt +++ b/backend/src/main/kotlin/dev/dres/DRES.kt @@ -42,7 +42,7 @@ import kotlin.system.exitProcess */ object DRES { /** Version of DRES. */ - const val VERSION = "2.0.0" + const val VERSION = "2.0.1" /** Application root; should be relative to JAR file or classes path. */ val APPLICATION_ROOT: Path = diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt index d881f49c..ec935beb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationDownloadHandler.kt @@ -1,7 +1,7 @@ package dev.dres.api.rest.handler.download -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.handler.GetRestHandler +import dev.dres.api.rest.types.evaluation.ApiEvaluation import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException import dev.dres.data.model.run.DbEvaluation @@ -18,7 +18,7 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class EvaluationDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), GetRestHandler { +class EvaluationDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), GetRestHandler { /** The route of this [EvaluationDownloadHandler]. */ override val route = "download/evaluation/{evaluationId}" @@ -32,14 +32,14 @@ class EvaluationDownloadHandler(private val store: TransientEntityStore) : Abstr OpenApiParam("evaluationId", String::class, "The evaluation ID.", required = true) ], responses = [ - OpenApiResponse("200", [OpenApiContent(String::class, type = "application/json")]), + OpenApiResponse("200", [OpenApiContent(ApiEvaluation::class)]), OpenApiResponse("400", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("401", [OpenApiContent(ErrorStatus::class)]), OpenApiResponse("404", [OpenApiContent(ErrorStatus::class)]) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): String { + override fun doGet(ctx: Context): ApiEvaluation { /* Obtain run id and run. */ val evaluationId = ctx.pathParamMap().getOrElse("evaluationId") { throw ErrorStatusException(400, "Parameter 'evaluationId' is missing!'", ctx) } val evaluation = this.store.transactional(true) { @@ -51,7 +51,6 @@ class EvaluationDownloadHandler(private val store: TransientEntityStore) : Abstr ctx.header("Content-Disposition", "attachment; filename=\"run-${evaluationId}.json\"") /* Return value. */ - val mapper = jacksonObjectMapper() - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(evaluation) + return evaluation } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt index c222f7d0..28bb108b 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/download/EvaluationTemplateDownloadHandler.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import dev.dres.api.rest.handler.GetRestHandler import dev.dres.api.rest.types.status.ErrorStatus import dev.dres.api.rest.types.status.ErrorStatusException +import dev.dres.api.rest.types.template.ApiEvaluationTemplate import dev.dres.data.model.template.DbEvaluationTemplate import io.javalin.http.Context import io.javalin.openapi.* @@ -18,7 +19,8 @@ import kotlinx.dnq.query.query * @author Ralph Gasser * @version 1.0.0 */ -class EvaluationTemplateDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), GetRestHandler { +class EvaluationTemplateDownloadHandler(private val store: TransientEntityStore) : AbstractDownloadHandler(), + GetRestHandler { /** The route of this [EvaluationTemplateDownloadHandler]. */ override val route = "download/template/{templateId}" @@ -39,7 +41,7 @@ class EvaluationTemplateDownloadHandler(private val store: TransientEntityStore) ], methods = [HttpMethod.GET] ) - override fun doGet(ctx: Context): String { + override fun doGet(ctx: Context): ApiEvaluationTemplate { /* Obtain run id and run. */ val templateId = ctx.pathParamMap()["templateId"] ?: throw ErrorStatusException(400, "Parameter 'templateId' is missing!'", ctx) val template = this.store.transactional(true) { @@ -51,7 +53,6 @@ class EvaluationTemplateDownloadHandler(private val store: TransientEntityStore) ctx.header("Content-Disposition", "attachment; filename=\"evaluation-template-${templateId}.json") /* Return value. */ - val mapper = jacksonObjectMapper() - return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(template) + return template } } diff --git a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt index aabc1972..f9dfceeb 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/handler/evaluation/viewer/GetTaskHintHandler.kt @@ -2,7 +2,6 @@ package dev.dres.api.rest.handler.evaluation.viewer import dev.dres.DRES import dev.dres.api.rest.handler.GetRestHandler -import dev.dres.api.rest.handler.evaluation.viewer.AbstractEvaluationViewerHandler import dev.dres.api.rest.types.ViewerInfo import dev.dres.api.rest.types.template.tasks.ApiHintContent import dev.dres.api.rest.types.status.ErrorStatus @@ -140,8 +139,8 @@ class GetTaskHintHandler(private val store: TransientEntityStore, private val ca private fun ApiHint.toContentElement(): ApiContentElement { //TODO find a better place for this lookup - val item = this.mediaItem?.let {itemId -> - DbMediaItem.filter { it.mediaItemId eq itemId }.firstOrNull() + val item = this.item?.let { item -> + DbMediaItem.filter { it.mediaItemId eq item.mediaItemId }.firstOrNull() } val range = if (item?.fps != null) { this.range?.toTemporalRange(item.fps!!) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt index 031a9888..ad8500e1 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/evaluation/ApiTask.kt @@ -1,6 +1,8 @@ package dev.dres.api.rest.types.evaluation import dev.dres.api.rest.types.evaluation.submission.ApiAnswerSet +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission +import dev.dres.api.rest.types.evaluation.submission.ApiSubmission import dev.dres.data.model.run.DbTask import dev.dres.data.model.run.TaskId import dev.dres.data.model.template.TemplateId @@ -19,5 +21,5 @@ data class ApiTask( val templateId: TemplateId, val started: Long?, val ended: Long?, - val submissions: List -) \ No newline at end of file + val submissions: List +) diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt index 4d95f38d..646596c0 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiHint.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.template.tasks +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.data.model.template.* import kotlinx.serialization.Serializable @@ -65,12 +66,7 @@ data class ApiHint( * * This is the reference to the media item */ - val mediaItem: String? = null, - - /** - * This is a reference to the media item's name, in case there is one. - */ - val mediaItemName: String? = null, + val item: ApiMediaItem? = null, /** * In case [type] is diff --git a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt index 4769ab6d..bee07a34 100644 --- a/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt +++ b/backend/src/main/kotlin/dev/dres/api/rest/types/template/tasks/ApiTarget.kt @@ -1,5 +1,6 @@ package dev.dres.api.rest.types.template.tasks +import dev.dres.api.rest.types.collection.ApiMediaItem import dev.dres.api.rest.types.collection.time.ApiTemporalRange import dev.dres.data.model.template.task.DbTaskTemplateTarget import kotlinx.serialization.Serializable @@ -19,7 +20,7 @@ data class ApiTarget( val target: String? = null, val range: ApiTemporalRange? = null, /** - * The name of the target, which is defined for certain types (e.g. [ApiTargetType]-MEDIA_TIEM ) + * The target as item, which is only defined for certain types (e.g. [ApiTargetType]-MEDIA_TIEM ) */ - val name: String? = null + val item: ApiMediaItem? = null ) diff --git a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt index c5b70370..69d3be52 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/run/DbTask.kt @@ -1,6 +1,7 @@ package dev.dres.data.model.run import dev.dres.api.rest.types.evaluation.ApiTask +import dev.dres.api.rest.types.evaluation.submission.ApiClientSubmission import dev.dres.data.model.PersistentEntity import dev.dres.data.model.submissions.AnswerSet import dev.dres.data.model.template.task.DbTaskTemplate @@ -11,6 +12,7 @@ import dev.dres.data.model.template.team.DbTeam import jetbrains.exodus.entitystore.Entity import kotlinx.dnq.* import kotlinx.dnq.query.asSequence +import kotlinx.dnq.query.mapDistinct import kotlinx.dnq.util.isInstanceOf import java.lang.IllegalStateException @@ -47,9 +49,12 @@ class DbTask(entity: Entity) : PersistentEntity(entity) { /** Link to a [DbTeam] this [DbTask] was created for. Can be NULL!*/ var team by xdLink0_1(DbTeam) - /** List of [DbSubmission]s received by this [DbTask]. */ + /** List of [DbAnswerSet]s received by this [DbTask]. */ val answerSets by xdLink0_N(DbAnswerSet::task) + /** List of [DbSubmission]s received by this [DbTask].*/ + fun submissions() = this.answerSets.asSequence().map{it.submission}.toList() + /** * Converts this [DbTask] to a RESTful API representation [ApiTask]. * @@ -62,6 +67,6 @@ class DbTask(entity: Entity) : PersistentEntity(entity) { templateId = this.template.id, started = this.started, ended = this.ended, - submissions = this.answerSets.asSequence().map { it.toApi() }.toList() + submissions = submissions().map { it.toApi() }.toList() ) -} \ No newline at end of file +} diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt index 4150b3d9..9712f820 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbHint.kt @@ -65,8 +65,7 @@ class DbHint(entity: Entity) : XdEntity(entity) { start = this.start, end = this.end, description = this.text, - mediaItem = this.item?.id, - mediaItemName = this.item?.name, + item = this.item?.toApi(), dataType = this.type.mimeType, path = this.path, range = this.range?.let { ApiTemporalRange(it) } diff --git a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt index f05f6c6a..12bce352 100644 --- a/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt +++ b/backend/src/main/kotlin/dev/dres/data/model/template/task/DbTaskTemplateTarget.kt @@ -60,7 +60,7 @@ class DbTaskTemplateTarget(entity: Entity) : XdEntity(entity) { type=this.type.toApi(), target = this.item?.id, range= this.range?.let { ApiTemporalRange(it) }, - name= this.item?.name + item = this.item?.toApi() ) DbTargetType.TEXT -> ApiTarget(this.type.toApi(), this.text) else -> throw IllegalStateException("Task description of type ${this.type.description} is not supported.") diff --git a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt index eb81106c..fb0b877e 100644 --- a/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt +++ b/backend/src/main/kotlin/dev/dres/mgmt/TemplateManager.kt @@ -231,8 +231,9 @@ object TemplateManager { task.hints.add(DbHint.new { this.type = hint.type.toDb() this.item = - hint.mediaItem?.let { DbMediaItem.query(DbMediaItem::id eq hint.mediaItem).firstOrNull() } + hint.item?.let { DbMediaItem.query(DbMediaItem::id eq hint.item.mediaItemId).firstOrNull() } this.text = hint.description + this.path = hint.path this.start = hint.start this.end = hint.end diff --git a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt index 7c740aca..2dc5b44c 100644 --- a/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt +++ b/backend/src/main/kotlin/dev/dres/run/RunExecutor.kt @@ -59,9 +59,19 @@ object RunExecutor { DbEvaluation.filter { (it.ended eq null) }.asSequence().forEach { evaluation -> try { this.schedule(evaluation.toRunManager(store)) /* Re-schedule evaluations. */ - } catch (e: IllegalStateException) { - logger.error("Could not re-schedule previous run: ${e.message}") - evaluation.ended = System.currentTimeMillis() + } catch (e: RuntimeException) { + when (e) { + is IllegalStateException, + is IllegalArgumentException -> { + logger.error("Could not re-schedule previous run: ${e.message}") + evaluation.ended = System.currentTimeMillis() + } + + else -> { + logger.error("Fatal error during re-scheduling of previous run (${evaluation.evaluationId}): ${e.message}") + throw e + } + } } } } diff --git a/doc/oas-client.json b/doc/oas-client.json index 37ca2dc6..53328909 100644 --- a/doc/oas-client.json +++ b/doc/oas-client.json @@ -2,8 +2,8 @@ "openapi" : "3.0.3", "info" : { "title" : "DRES Client API", - "version" : "2.0.0-RC5", - "description" : "Client API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0-RC5" + "version" : "2.0.1", + "description" : "Client API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.1" }, "paths" : { "/api/v2/client/evaluation/currentTask/{evaluationId}" : { @@ -1342,7 +1342,7 @@ "submissions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" + "$ref" : "#/components/schemas/ApiSubmission" } } }, @@ -2021,11 +2021,8 @@ "dataType" : { "type" : "string" }, - "mediaItem" : { - "type" : "string" - }, - "mediaItemName" : { - "type" : "string" + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" @@ -2069,8 +2066,8 @@ "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" }, - "name" : { - "type" : "string" + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" } }, "required" : [ "type" ] diff --git a/doc/oas.json b/doc/oas.json index d62691e4..e822ddb8 100644 --- a/doc/oas.json +++ b/doc/oas.json @@ -2,8 +2,8 @@ "openapi" : "3.0.3", "info" : { "title" : "DRES API", - "version" : "2.0.0-RC5", - "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.0-RC5", + "version" : "2.0.1", + "description" : "API for DRES (Distributed Retrieval Evaluation Server), Version 2.0.1", "contact" : { "name" : "The DRES Dev Team", "url" : "https://dres.dev" @@ -594,9 +594,9 @@ "200" : { "description" : "OK", "content" : { - "text/plain" : { + "application/json" : { "schema" : { - "type" : "string" + "$ref" : "#/components/schemas/ApiEvaluation" } } } @@ -5756,7 +5756,7 @@ "submissions" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/ApiAnswerSet" + "$ref" : "#/components/schemas/ApiSubmission" } } }, @@ -6435,11 +6435,8 @@ "dataType" : { "type" : "string" }, - "mediaItem" : { - "type" : "string" - }, - "mediaItemName" : { - "type" : "string" + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" }, "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" @@ -6483,8 +6480,8 @@ "range" : { "$ref" : "#/components/schemas/ApiTemporalRange" }, - "name" : { - "type" : "string" + "item" : { + "$ref" : "#/components/schemas/ApiMediaItem" } }, "required" : [ "type" ] diff --git a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts index b8734072..795ace5a 100644 --- a/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts +++ b/frontend/src/app/competition/competition-builder/competition-builder-task-dialog/competition-form.builder.ts @@ -191,7 +191,7 @@ export class CompetitionFormBuilder { type: c.get('type').value, start: c.get('start').value, end: c.get('end').value, - mediaItem: c.get('mediaItem')?.value?.mediaItemId ?? null, + item: c.get('mediaItem')?.value ?? null, range: c.get('segment_start') && c.get('segment_end') ? ({ @@ -236,7 +236,7 @@ export class CompetitionFormBuilder { type: c.get('type').value, start: c.get('start').value, end: c.get('end').value, - mediaItem: c.get('mediaItem')?.value?.mediaItemId ?? null, + item: c.get('mediaItem')?.value ?? null, range: c.get('segment_start') && c.get('segment_end') ? ({ @@ -491,13 +491,13 @@ export class CompetitionFormBuilder { * @return The new {@link FormGroup} */ private imageItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { - if(initialize?.path && !initialize?.mediaItem){ + if(initialize?.path && !initialize?.item){ /* Handle external image */ return this.externalImageItemComponentForm(index, initialize); } const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); if ( - !initialize?.mediaItem && + !initialize?.item && (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { mediaItemFormControl.setValue((this.form.get('target') as UntypedFormArray).controls[0].get('mediaItem').value, {emitEvent: false}); @@ -515,9 +515,9 @@ export class CompetitionFormBuilder { ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data?.collectionId) { + if (initialize?.item && this.data?.collectionId) { this.collectionService - .getApiV2MediaItemByMediaItemId(initialize?.mediaItem) + .getApiV2MediaItemByMediaItemId(initialize?.item.mediaItemId) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s, {emitEvent: false}); @@ -546,14 +546,14 @@ export class CompetitionFormBuilder { * @return The new {@link FormGroup} */ private videoItemComponentForm(index: number, initialize?: ApiHint): UntypedFormGroup { - if(initialize?.path && !initialize?.mediaItem){ + if(initialize?.path && !initialize?.item){ /* handle external video */ return this.externalVideoItemComponentForm(index, initialize); } /* Initialize media item based on target. */ const mediaItemFormControl = new UntypedFormControl(null, [Validators.required, RequireMatch]); if ( - !initialize?.mediaItem && + !initialize?.item && (this.taskType.targetOption === 'SINGLE_MEDIA_SEGMENT' || this.taskType.targetOption === 'SINGLE_MEDIA_ITEM') ) { mediaItemFormControl.setValue((this.form.get('target') as UntypedFormArray).controls[0].get('mediaItem').value, {emitEvent: false}); @@ -571,9 +571,9 @@ export class CompetitionFormBuilder { ); /* Load media item from API. */ - if (initialize?.mediaItem && this.data?.collectionId) { + if (initialize?.item && this.data?.collectionId) { this.collectionService - .getApiV2MediaItemByMediaItemId(initialize.mediaItem) + .getApiV2MediaItemByMediaItemId(initialize.item.mediaItemId) .pipe(first()) .subscribe((s) => { mediaItemFormControl.setValue(s, {emitEvent: false}); diff --git a/frontend/src/app/services/pipes/not-in-list-filter.pipe.ts b/frontend/src/app/services/pipes/not-in-list-filter.pipe.ts new file mode 100644 index 00000000..a60c1e5f --- /dev/null +++ b/frontend/src/app/services/pipes/not-in-list-filter.pipe.ts @@ -0,0 +1,18 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'notInListFilter' +}) +export class NotInListFilterPipe implements PipeTransform { + + transform(list: any[], filter: any): any[] { + if(!list){ + return []; + } + if(!filter){ + return list; + } + return list.filter(it => it != filter); + } + +} diff --git a/frontend/src/app/services/services.module.ts b/frontend/src/app/services/services.module.ts index 2eb79b93..bf25de64 100644 --- a/frontend/src/app/services/services.module.ts +++ b/frontend/src/app/services/services.module.ts @@ -23,6 +23,7 @@ import { OrderByPipe } from './pipes/order-by.pipe'; import { FilterNotInPipe } from './pipes/filter-not-in.pipe'; import { UnderscoreWordBreakPipe } from './pipes/underscore-wordbreak.pipe'; import { SubmissionsOfPipe } from './pipes/submissions-of.pipe'; +import { NotInListFilterPipe } from './pipes/not-in-list-filter.pipe'; /** * Provides the {@link AppConfig} reference. @@ -59,7 +60,8 @@ export function initializeApiConfig(appConfig: AppConfig) { OrderByPipe, FilterNotInPipe, UnderscoreWordBreakPipe, - SubmissionsOfPipe + SubmissionsOfPipe, + NotInListFilterPipe ], declarations: [ RoundPipePipe, @@ -81,6 +83,7 @@ export function initializeApiConfig(appConfig: AppConfig) { FilterNotInPipe, UnderscoreWordBreakPipe, SubmissionsOfPipe, + NotInListFilterPipe, ], providers: [ AuthenticationService, diff --git a/frontend/src/app/shared/search-box/search-box.component.html b/frontend/src/app/shared/search-box/search-box.component.html new file mode 100644 index 00000000..5ac288b3 --- /dev/null +++ b/frontend/src/app/shared/search-box/search-box.component.html @@ -0,0 +1,16 @@ + + + search + + + diff --git a/frontend/src/app/shared/search-box/search-box.component.scss b/frontend/src/app/shared/search-box/search-box.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/shared/search-box/search-box.component.ts b/frontend/src/app/shared/search-box/search-box.component.ts new file mode 100644 index 00000000..3c12d10e --- /dev/null +++ b/frontend/src/app/shared/search-box/search-box.component.ts @@ -0,0 +1,27 @@ +import { Component, EventEmitter, OnInit, Output } from "@angular/core"; + +@Component({ + selector: 'app-search-box', + templateUrl: './search-box.component.html', + styleUrls: ['./search-box.component.scss'] +}) +export class SearchBoxComponent{ + // Source: https://angular-htpgvx.stackblitz.io + @Output() filterChanged = new EventEmitter(); + public searchBoxActive = false; + filter: string; + + onFilterClear(){ + this.filter = ''; + this.searchBoxActive = false; + this.filterChanged.emit(this.filter); + } + + onTextChanged(){ + this.filterChanged.emit(this.filter); + } + + public clear(){ + this.onFilterClear(); + } +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index b725d905..902aaed5 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -17,6 +17,9 @@ import { ServerInfoComponent } from './server-info/server-info.component'; import { TargetMediaViewerComponent } from './target-media-viewer/target-media-viewer.component'; import { MediaItemViewerComponent } from './media-item-viewer/media-item-viewer.component'; import { InformationDialogComponent } from './information-dialog/information-dialog.component'; +import { SearchBoxComponent } from './search-box/search-box.component'; +import { MatInputModule } from "@angular/material/input"; +import { FormsModule } from "@angular/forms"; @NgModule({ declarations: [ @@ -31,6 +34,7 @@ import { InformationDialogComponent } from './information-dialog/information-dia TargetMediaViewerComponent, MediaItemViewerComponent, InformationDialogComponent, + SearchBoxComponent, ], exports: [ BackButtonComponent, @@ -42,8 +46,9 @@ import { InformationDialogComponent } from './information-dialog/information-dia UploadJsonButtonComponent, ActionableDynamicTable, TargetMediaViewerComponent, - MediaItemViewerComponent + MediaItemViewerComponent, + SearchBoxComponent ], - imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule] + imports: [CommonModule, MatButtonModule, ServicesModule, MatIconModule, MatTooltipModule, MatDialogModule, MatTableModule, MatInputModule, FormsModule] }) export class SharedModule {} diff --git a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html index 832d043b..782851bc 100644 --- a/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html +++ b/frontend/src/app/template/template-builder/components/team-builder-dialog/team-builder-dialog.component.html @@ -1,5 +1,5 @@

Add team

-
+