diff --git a/apps/worker/sync/src/app/processors/check-encounters/check-encounters.processor.ts b/apps/worker/sync/src/app/processors/check-encounters/check-encounters.processor.ts index 0f060f53d2..1e4cc5d026 100644 --- a/apps/worker/sync/src/app/processors/check-encounters/check-encounters.processor.ts +++ b/apps/worker/sync/src/app/processors/check-encounters/check-encounters.processor.ts @@ -62,18 +62,8 @@ const includes = [ include: [ { required: true, - attributes: ['id'], + attributes: ['id', 'eventId'], model: SubEventCompetition, - include: [ - { - required: true, - attributes: ['id', 'visualCode'], - model: EventCompetition, - where: { - checkEncounterForFilledIn: true, - }, - }, - ], }, ], }, @@ -172,6 +162,7 @@ export class CheckEncounterProcessor { // Processing encounters for (const encounter of chunk) { + await this.loadEvent(encounter); await this._syncEncounter(encounter, page); encountersProcessed++; } @@ -192,6 +183,7 @@ export class CheckEncounterProcessor { cronJob.amount++; cronJob.lastRun = new Date(); + cronJob.running = false; await cronJob.save(); this.logger.log('Synced encounters'); @@ -206,12 +198,17 @@ export class CheckEncounterProcessor { include: includes, }); + await this.loadEvent(encounter); + if (!encounter) { this.logger.error(`Encounter ${job.data.encounterId} not found`); return; } + + this.logger.debug(encounter.drawCompetition.subEventCompetition.eventCompetition.toJSON()); + // Create browser - const browser = await getBrowser(false); + const browser = await getBrowser(); try { const page = await browser.newPage(); page.setDefaultTimeout(10000); @@ -236,6 +233,25 @@ export class CheckEncounterProcessor { } } + private async loadEvent(encounter: EncounterCompetition) { + const event = await encounter.drawCompetition.subEventCompetition.getEventCompetition({ + attributes: ['id', 'visualCode', 'contactEmail', 'name'], + where: { + checkEncounterForFilledIn: true, + }, + include: [ + { + model: Player, + as: 'contact', + attributes: ['id', 'email'], + }, + ], + }); + + // set the event + encounter.drawCompetition.subEventCompetition.eventCompetition = event; + } + private async _syncEncounter(encounter: EncounterCompetition, page: Page) { const url = await gotoEncounterPage({ page }, encounter); this.logger.debug(`Syncing encounter ${url}`); @@ -255,6 +271,11 @@ export class CheckEncounterProcessor { `Encounter passed ${hoursPassed} hours ago, entered: ${entered}, accepted: ${accepted}, has comments: ${hasComment} ( ${url} )`, ); + // if we have a comment notify the event contact + if (hasComment) { + this.notificationService.notifyEncounterHasComment(encounter); + } + // not entered and passed 24 hours and no comment if (!entered && hoursPassed > 24 && !hasComment) { this.notificationService.notifyEncounterNotEntered(encounter); diff --git a/database/migrations/20240921091411-notiications for when there is a comment.js b/database/migrations/20240921091411-notiications for when there is a comment.js new file mode 100644 index 0000000000..20719a5a5c --- /dev/null +++ b/database/migrations/20240921091411-notiications for when there is a comment.js @@ -0,0 +1,77 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, sequelize) => { + return queryInterface.sequelize.transaction(async (t) => { + try { + await queryInterface.addColumn( + { + tableName: 'Settings', + schema: 'personal', + }, + 'encounterHasCommentNotification', + { + type: sequelize.DataTypes.INTEGER, + allowNull: false, + defaultValue: 2, + }, + { transaction: t }, + ); + + await queryInterface.addColumn( + { + tableName: 'EventCompetitions', + schema: 'event', + }, + 'contactId', + { + type: sequelize.DataTypes.UUID, + allowNull: true, + references: { + model: { + tableName: 'Players', + schema: 'public', + }, + key: 'id', + }, + }, + { transaction: t }, + ); + + + } catch (err) { + console.error('We errored with', err?.message ?? err); + t.rollback(); + } + }); + }, + + down: async (queryInterface) => { + return queryInterface.sequelize.transaction(async (t) => { + try { + await queryInterface.removeColumn( + { + tableName: 'Settings', + schema: 'personal', + }, + 'encounterHasCommentNotification', + { transaction: t }, + ); + + await queryInterface.removeColumn( + { + tableName: 'EventCompetitions', + schema: 'event', + }, + 'contactId', + { transaction: t }, + ); + } catch (err) { + console.error('We errored with', err); + t.rollback(); + } + }); + }, +}; diff --git a/libs/backend/database/src/models/event/competition/event-competition.model.ts b/libs/backend/database/src/models/event/competition/event-competition.model.ts index 70e8c864e9..a589cff67d 100644 --- a/libs/backend/database/src/models/event/competition/event-competition.model.ts +++ b/libs/backend/database/src/models/event/competition/event-competition.model.ts @@ -1,6 +1,8 @@ import { LevelType, UsedRankingTiming } from '@badman/utils'; import { Field, ID, InputType, Int, ObjectType, OmitType, PartialType } from '@nestjs/graphql'; import { + BelongsToGetAssociationMixin, + BelongsToSetAssociationMixin, CreationOptional, HasManyAddAssociationMixin, HasManyAddAssociationsMixin, @@ -15,6 +17,7 @@ import { InferCreationAttributes, } from 'sequelize'; import { + BelongsTo, Column, DataType, Default, @@ -36,6 +39,7 @@ import { Slugify, } from '../../../types'; import { Relation } from '../../../wrapper'; +import { Player } from '../../player.model'; import { Role } from '../../security'; import { AvailabilityException } from '../availability.model'; import { Comment } from './../../comment.model'; @@ -64,7 +68,7 @@ export class EventCompetition extends Model< override createdAt?: Date; @Unique('EventCompetitions_unique_constraint') - @Field(() => String, { nullable: true }) + @Field(() => String, { nullable: false }) @Column(DataType.STRING) name?: string; @@ -95,7 +99,7 @@ export class EventCompetition extends Model< @Field(() => Date, { nullable: true }) @Column(DataType.DATE) - changeCloseDatePeriod2?: Date; + changeCloseDatePeriod2?: Date; @Field(() => Date, { nullable: true }) @Column(DataType.DATE) @@ -115,6 +119,17 @@ export class EventCompetition extends Model< @Column(DataType.STRING) contactEmail?: string; + @Field(() => ID, { nullable: true }) + @Column(DataType.UUIDV4) + contactId?: string; + + @Field(() => Player, { nullable: true }) + @BelongsTo(() => Player, { + foreignKey: 'contactId', + constraints: false, + }) + contact?: Relation; + @Field(() => [Comment], { nullable: true }) @HasMany(() => Comment, { foreignKey: 'linkId', @@ -252,6 +267,10 @@ export class EventCompetition extends Model< hasRole!: HasManyHasAssociationMixin; hasRoles!: HasManyHasAssociationsMixin; countRoles!: HasManyCountAssociationsMixin; + + // Belongs to Contact + getContact!: BelongsToGetAssociationMixin; + setContact!: BelongsToSetAssociationMixin; } @InputType() @@ -264,6 +283,7 @@ export class EventCompetitionUpdateInput extends PartialType( 'roles', 'exceptions', 'infoEvents', + 'contact', 'meta', ] as const), InputType, diff --git a/libs/backend/database/src/models/personal/setting.model.ts b/libs/backend/database/src/models/personal/setting.model.ts index ff173d1d3a..2400a04123 100644 --- a/libs/backend/database/src/models/personal/setting.model.ts +++ b/libs/backend/database/src/models/personal/setting.model.ts @@ -97,6 +97,13 @@ export class Setting extends Model { defaultValue: NotificationType.NONE, }) encounterChangeFinishedNotification!: NotificationType; + + @Field(() => Int) + @Column({ + type: DataType.INTEGER, + defaultValue: NotificationType.NONE, + }) + encounterHasCommentNotification!: NotificationType; @Field(() => Int) @Column({ diff --git a/libs/backend/mailing/src/compile/templates/hasComment/html.pug b/libs/backend/mailing/src/compile/templates/hasComment/html.pug new file mode 100644 index 0000000000..aad24d6657 --- /dev/null +++ b/libs/backend/mailing/src/compile/templates/hasComment/html.pug @@ -0,0 +1,29 @@ +extends ../../layouts/layout.pug + +block title + h1.text-center + | Ontmoeting heeft een opmerking + +block content + p.text-center + p + | Beste #{ contact }, + br + br + | + | De ontmoeting + strong #{ encounter.home.name } + | tegen + strong #{ encounter.away.name } + | op + strong #{ date } + | heeft een opmerking. + br + p + a(href=`${url}`) + | Ga naar ontmoeting + p + | Met sportieve groeten, + br + | + | Badman \ No newline at end of file diff --git a/libs/backend/mailing/src/compile/templates/notaccepted/html.pug b/libs/backend/mailing/src/compile/templates/notaccepted/html.pug index 7db391953e..ffdaf4fe4d 100644 --- a/libs/backend/mailing/src/compile/templates/notaccepted/html.pug +++ b/libs/backend/mailing/src/compile/templates/notaccepted/html.pug @@ -26,11 +26,4 @@ block content | Met sportieve groeten, br | - | Badman - p - | Disclaimer: - br - small - | - | Deze notificatie is nog in beta. Mocht je vragen of opmerkingen hebben, laat het ons weten via - a(href="mailto:info@badmintonvlaanderen.be") badmintonvlaanderen + | Badman \ No newline at end of file diff --git a/libs/backend/mailing/src/compile/templates/notentered/html.pug b/libs/backend/mailing/src/compile/templates/notentered/html.pug index 8149768f13..ceeda54017 100644 --- a/libs/backend/mailing/src/compile/templates/notentered/html.pug +++ b/libs/backend/mailing/src/compile/templates/notentered/html.pug @@ -11,11 +11,11 @@ block content br br | - | De ontmoeting + | De ontmoeting strong #{ encounter.home.name } - | tegen + | tegen strong #{ encounter.away.name } - | op + | op strong #{ date } | is nog niet ingevuld. br @@ -27,9 +27,3 @@ block content br | | Badman - p - | Disclaimer: - br - | - | Deze notificatie is nog in beta. Mocht je vragen of opmerkingen hebben, laat het ons weten via - a(href="mailto:info@badmintonvlaanderen.be") badmintonvlaanderen diff --git a/libs/backend/mailing/src/compile/templates/synEncounterFailed/html.pug b/libs/backend/mailing/src/compile/templates/synEncounterFailed/html.pug index 32f17ec629..a731ded6d3 100644 --- a/libs/backend/mailing/src/compile/templates/synEncounterFailed/html.pug +++ b/libs/backend/mailing/src/compile/templates/synEncounterFailed/html.pug @@ -26,11 +26,4 @@ block content | Met sportieve groeten, br | - | Badman - p - | Disclaimer: - br - small - | - | Deze notificatie is nog in beta. Mocht je vragen of opmerkingen hebben, laat het ons weten via - a(href="mailto:info@badmintonvlaanderen.be") badmintonvlaanderen + | Badman \ No newline at end of file diff --git a/libs/backend/mailing/src/compile/templates/syncFinished/html.pug b/libs/backend/mailing/src/compile/templates/syncFinished/html.pug index d2e33bb0b3..00a8182d47 100644 --- a/libs/backend/mailing/src/compile/templates/syncFinished/html.pug +++ b/libs/backend/mailing/src/compile/templates/syncFinished/html.pug @@ -29,11 +29,4 @@ block content | Met sportieve groeten, br | - | Badman - p - | Disclaimer: - br - small - | - | Deze notificatie is nog in beta. Mocht je vragen of opmerkingen hebben, laat het ons weten via - a(href="mailto:info@badmintonvlaanderen.be") badmintonvlaanderen + | Badman \ No newline at end of file diff --git a/libs/backend/mailing/src/services/mailing/mailing.service.ts b/libs/backend/mailing/src/services/mailing/mailing.service.ts index 848aa717ff..1cca3e80c4 100644 --- a/libs/backend/mailing/src/services/mailing/mailing.service.ts +++ b/libs/backend/mailing/src/services/mailing/mailing.service.ts @@ -319,6 +319,40 @@ export class MailingService { await this._sendMail(options); } + + async sendHasCommentMail( + to: { + fullName: string; + email: string; + slug: string; + }, + encounter: EncounterCompetition, + url: string, + ) { + moment.locale('nl-be'); + const options = { + from: 'info@badman.app', + to: to.email, + subject: `Ontmoeting ${encounter.home?.name} tegen ${encounter.away?.name} heeft een opmerking`, + template: 'hasComment', + context: { + encounter: encounter.toJSON(), + url, + contact: to.fullName, + date: moment(encounter.date).tz('Europe/Brussels').format('LLLL'), + settingsSlug: to.slug, + }, + } as MailOptions<{ + encounter: EncounterCompetition; + url: string; + contact: string; + date: string; + settingsSlug: string; + }>; + + await this._sendMail(options); + } + async sendNotAcceptedMail( to: { fullName: string; diff --git a/libs/backend/notifications/src/notifiers/encounterEntered/hasComment.ts b/libs/backend/notifications/src/notifiers/encounterEntered/hasComment.ts new file mode 100644 index 0000000000..1f85e99160 --- /dev/null +++ b/libs/backend/notifications/src/notifiers/encounterEntered/hasComment.ts @@ -0,0 +1,89 @@ +import { EncounterCompetition, NotificationOptionsTypes, Player } from '@badman/backend-database'; +import { Notifier } from '../notifier.base'; +import * as webPush from 'web-push'; + +export class CompetitionEncounterHasCommentNotifier extends Notifier< + { + encounter: EncounterCompetition; + }, + { + email: string; + url: string; + } +> { + protected linkType = 'encounterCompetition'; + protected type: keyof NotificationOptionsTypes = 'encounterHasCommentNotification'; + protected override allowedAmount = 1; + + private readonly options = (url: string, encounter: EncounterCompetition) => { + return { + notification: { + title: 'Opmerking geplaatst', + body: `Ontmoeting ${encounter.home?.name} tegen ${encounter.away?.name} heeft een opmerking`, + actions: [{ action: 'goto', title: 'Ga naar wedstrijd' }], + data: { + onActionClick: { + default: { operation: 'openWindow', url: url }, + goto: { operation: 'openWindow', url: url }, + }, + }, + }, + } as webPush.RequestOptions; + }; + + async notifyPush( + player: Player, + data: { encounter: EncounterCompetition }, + args?: { email: string; url: string }, + ): Promise { + this.logger.debug(`Sending Push to ${player.fullName}`); + if (!args?.url) { + throw new Error('No url provided'); + } + + await this.pushService.sendNotification(player, this.options(args.url, data.encounter)); + } + + async notifyEmail( + player: Player, + data: { encounter: EncounterCompetition }, + args?: { email: string; url: string }, + ): Promise { + this.logger.debug(`Sending Email to ${player.fullName}`); + const email = args?.email ?? player.email; + if (!email) { + this.logger.debug(`No email found for ${player.fullName}`); + return; + } + + if (!player?.slug) { + this.logger.debug(`No slug found for ${player.fullName}`); + return; + } + + if (!args?.url) { + throw new Error('No url provided'); + } + + await this.mailing.sendHasCommentMail( + { + fullName: player.fullName, + email, + slug: player.slug, + }, + data.encounter, + args.url, + ); + } + + notifySms( + player: Player, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + data: { encounter: EncounterCompetition }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + args?: { email: string }, + ): Promise { + this.logger.debug(`Sending Sms to ${player.fullName}`); + return Promise.resolve(); + } +} diff --git a/libs/backend/notifications/src/notifiers/encounterEntered/index.ts b/libs/backend/notifications/src/notifiers/encounterEntered/index.ts index 5eca599729..b03d8d4bbc 100644 --- a/libs/backend/notifications/src/notifiers/encounterEntered/index.ts +++ b/libs/backend/notifications/src/notifiers/encounterEntered/index.ts @@ -1,4 +1,3 @@ -// start:ng42.barrel +export * from './hasComment'; export * from './notAccepted'; export * from './notEntered'; -// end:ng42.barrel diff --git a/libs/backend/notifications/src/services/notification/notification.service.ts b/libs/backend/notifications/src/services/notification/notification.service.ts index ff5ad54bfe..7db6affabd 100644 --- a/libs/backend/notifications/src/services/notification/notification.service.ts +++ b/libs/backend/notifications/src/services/notification/notification.service.ts @@ -21,6 +21,7 @@ import { EventSyncedFailedNotifier, CompetitionEncounterChangeFinishRequestNotifier, SyncEncounterFailed, + CompetitionEncounterHasCommentNotifier, } from '../../notifiers'; import { PushService } from '../push'; import { ConfigService } from '@nestjs/config'; @@ -36,7 +37,7 @@ import { I18nTranslations } from '@badman/utils'; @Injectable() export class NotificationService { - private readonly logger = new Logger(NotificationService.name); + private readonly _logger = new Logger(NotificationService.name); constructor( private mailing: MailingService, @@ -168,14 +169,45 @@ export class NotificationService { const matchId = encounter.visualCode; const url = `https://www.toernooi.nl/sport/teammatch.aspx?id=${eventId}&match=${matchId}`; - if (homeTeam.captain && homeTeam.email) { - notifierNotEntered.notify( - homeTeam.captain, - encounter.id, - { encounter }, - { email: homeTeam.email ?? homeTeam.captain?.email, url }, - ); + if (!homeTeam.captain || !homeTeam.email) { + this._logger.error('Captain or email not found'); + return; + } + + notifierNotEntered.notify( + homeTeam.captain, + encounter.id, + { encounter }, + { email: homeTeam.email ?? homeTeam.captain?.email, url }, + ); + } + + async notifyEncounterHasComment(encounter: EncounterCompetition) { + const notifierNotEntered = new CompetitionEncounterHasCommentNotifier(this.mailing, this.push); + + const event = encounter.drawCompetition?.subEventCompetition?.eventCompetition; + if (!event) { + throw new Error('Event not found'); } + + // Property was loaded when sending notification + const eventId = event?.visualCode; + const matchId = encounter.visualCode; + const url = `https://www.toernooi.nl/sport/teammatch.aspx?id=${eventId}&match=${matchId}`; + const email = event.contactEmail ?? event.contact?.email; + + if (!email) { + this._logger.error('Email not found'); + return; + } + + let contact = event.contact; + + if (!contact?.email || !contact?.fullName || !contact?.slug) { + contact = (await Player.findByPk(event.contactId ?? event.contact?.id)) as Player; + } + + notifierNotEntered.notify(contact, encounter.id, { encounter }, { email, url }); } async notifyEncounterNotAccepted(encounter: EncounterCompetition) { diff --git a/libs/frontend/models/src/models/events/competition/event.model.ts b/libs/frontend/models/src/models/events/competition/event.model.ts index 88e3b1f3b3..ffa7fba225 100644 --- a/libs/frontend/models/src/models/events/competition/event.model.ts +++ b/libs/frontend/models/src/models/events/competition/event.model.ts @@ -7,6 +7,7 @@ import { Exception, InfoEvent } from '../../availibilty.model'; export class EventCompetition extends Event { season?: number; contactEmail?: string; + contactId?: string; subEventCompetitions?: SubEventCompetition[]; comments?: Comment[]; type?: LevelType; @@ -28,6 +29,7 @@ export class EventCompetition extends Event { super(args); this.season = args.season; this.contactEmail = args.contactEmail; + this.contactId = args.contactId; this.eventType = args.eventType ?? EventType.COMPETITION; this.type = args.type; this.teamMatcher = args.teamMatcher; diff --git a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.html b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.html index 6e241c2c46..f09b4c967f 100644 --- a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.html +++ b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.html @@ -28,6 +28,14 @@

General

Contact + +
@@ -131,7 +139,9 @@

- {{ 'all.competition.allow-competition' | translate }} + {{ + 'all.competition.allow-competition' | translate + }}
@@ -150,10 +160,10 @@

Sub events

- @for (subEvent of eventCompetition().subEventCompetitions; track subEvent; let i = $index) { + @for (subEvent of eventCompetition()?.subEventCompetitions; track subEvent; let i = $index) {
@@ -166,12 +176,12 @@

Sub events

} - +

Roles

diff --git a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.scss b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.scss index e2811d98e0..690cbc8604 100644 --- a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.scss +++ b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.scss @@ -49,6 +49,13 @@ h3 { } } +.contact { + display: flex; + flex-direction: row; + gap: 1rem; + flex: 1 1 auto; +} + .general { display: flex; flex-direction: column; @@ -82,7 +89,8 @@ h3 { } } -mat-form-field { +mat-form-field, +badman-player-search { flex: 1 1 auto; width: 100%; } diff --git a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.ts b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.ts index a053dca388..4d6a2c8288 100644 --- a/libs/frontend/pages/competition/event/src/pages/edit/edit.page.ts +++ b/libs/frontend/pages/competition/event/src/pages/edit/edit.page.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component, Signal, TemplateRef, computed, inject } from '@angular/core'; +import { Component, Signal, TemplateRef, inject } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -15,21 +15,23 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; -import { ActivatedRoute, Router, RouterModule } from '@angular/router'; +import { Router, RouterModule } from '@angular/router'; import { AddRoleComponent, EditRoleComponent, HasClaimComponent, PageHeaderComponent, + PlayerSearchComponent, SelectCountryComponent, SelectCountrystateComponent, } from '@badman/frontend-components'; -import { EventCompetition, Role } from '@badman/frontend-models'; +import { EventCompetition, Player, Role } from '@badman/frontend-models'; import { SeoService } from '@badman/frontend-seo'; import { LevelType, SecurityType } from '@badman/utils'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { Apollo, gql } from 'apollo-angular'; import { injectDestroy } from 'ngxtension/inject-destroy'; +import { injectRouteData } from 'ngxtension/inject-route-data'; import { BehaviorSubject, Subject, @@ -96,6 +98,7 @@ const roleQuery = gql` SelectCountryComponent, SelectCountrystateComponent, + PlayerSearchComponent, ], }) export class EditPageComponent { @@ -104,15 +107,12 @@ export class EditPageComponent { private readonly translateService = inject(TranslateService); private readonly snackBar = inject(MatSnackBar); private readonly seoService = inject(SeoService); - private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); private readonly breadcrumbService = inject(BreadcrumbService); private readonly apollo = inject(Apollo); private readonly dialog = inject(MatDialog); - private routeData = toSignal(this.route.data); - - eventCompetition = computed(() => this.routeData()?.['eventCompetition'] as EventCompetition); + eventCompetition = injectRouteData('eventCompetition'); public securityTypes: typeof SecurityType = SecurityType; @@ -133,11 +133,16 @@ export class EditPageComponent { constructor() { const compTitle = 'all.competition.title'; + const eventCompetition = this.eventCompetition(); + if (!eventCompetition) { + throw new Error('Event competition not found'); + } + this.translateService .get([compTitle]) .pipe(takeUntil(this.destroy$)) .subscribe((translations) => { - const eventCompetitionName = `${this.eventCompetition().name}`; + const eventCompetitionName = `${eventCompetition.name}`; this.breadcrumbService.set('competition', translations[compTitle]); this.breadcrumbService.set('@eventCompetition', eventCompetitionName); @@ -149,7 +154,7 @@ export class EditPageComponent { }); }); - this.setupFormGroup(this.eventCompetition()); + this.setupFormGroup(eventCompetition); this.roles = toSignal( this.roleChanged$.pipe( @@ -160,7 +165,7 @@ export class EditPageComponent { query: roleQuery, variables: { where: { - linkId: this.eventCompetition().id, + linkId: eventCompetition.id, linkType: 'competition', }, }, @@ -204,6 +209,7 @@ export class EditPageComponent { Validators.max(3000), ]), contactEmail: new FormControl(event.contactEmail, Validators.required), + contactId: new FormControl(event.contactId), checkEncounterForFilledIn: new FormControl(event.checkEncounterForFilledIn), teamMatcher: new FormControl(event.teamMatcher), @@ -252,7 +258,7 @@ export class EditPageComponent { } `, variables: { - id: this.eventCompetition().id, + id: this.eventCompetition()?.id, year: r, }, }) @@ -274,6 +280,7 @@ export class EditPageComponent { name: eventCompetition.name, season: eventCompetition.season, contactEmail: eventCompetition.contactEmail, + contactId: eventCompetition.contactId, teamMatcher: eventCompetition.teamMatcher, type: eventCompetition.type, state: eventCompetition.state, diff --git a/libs/frontend/pages/competition/event/src/resolvers/event.resolver.ts b/libs/frontend/pages/competition/event/src/resolvers/event.resolver.ts index 873f99c61a..de2c60a5ad 100644 --- a/libs/frontend/pages/competition/event/src/resolvers/event.resolver.ts +++ b/libs/frontend/pages/competition/event/src/resolvers/event.resolver.ts @@ -23,6 +23,7 @@ export const EVENT_QUERY = gql` official lastSync contactEmail + contactId teamMatcher type state