From d20904fbfe6a3769546b78a3d842ce30d6f39953 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Mon, 20 Nov 2023 13:53:46 +0100 Subject: [PATCH 1/9] add new date field to contribution_message --- .../ContributionMessage.ts | 59 +++++++++++++++++++ database/entity/ContributionMessage.ts | 2 +- ..._resubmission_date_contribution_message.ts | 12 ++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 database/entity/0077-add_resubmission_date_contribution_message/ContributionMessage.ts create mode 100644 database/migrations/0077-add_resubmission_date_contribution_message.ts diff --git a/database/entity/0077-add_resubmission_date_contribution_message/ContributionMessage.ts b/database/entity/0077-add_resubmission_date_contribution_message/ContributionMessage.ts new file mode 100644 index 000000000..2ea886dd5 --- /dev/null +++ b/database/entity/0077-add_resubmission_date_contribution_message/ContributionMessage.ts @@ -0,0 +1,59 @@ +import { + BaseEntity, + Column, + DeleteDateColumn, + Entity, + Index, + JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, +} from 'typeorm' +import { Contribution } from '../Contribution' +import { User } from '../User' + +@Entity('contribution_messages', { + engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci', +}) +export class ContributionMessage extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Index() + @Column({ name: 'contribution_id', unsigned: true, nullable: false }) + contributionId: number + + @ManyToOne(() => Contribution, (contribution) => contribution.messages) + @JoinColumn({ name: 'contribution_id' }) + contribution: Contribution + + @Column({ name: 'user_id', unsigned: true, nullable: false }) + userId: number + + @ManyToOne(() => User, (user) => user.messages) + @JoinColumn({ name: 'user_id' }) + user: User + + @Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' }) + message: string + + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) + createdAt: Date + + @Column({ type: 'datetime', default: null, nullable: true, name: 'updated_at' }) + updatedAt: Date + + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date | null + + @Column({ name: 'deleted_by', default: null, unsigned: true, nullable: true }) + deletedBy: number + + @Column({ type: 'datetime', name: 'resubmission_at', default: null, nullable: true }) + resubmissionAt: Date | null + + @Column({ length: 12, nullable: false, collation: 'utf8mb4_unicode_ci' }) + type: string + + @Column({ name: 'is_moderator', type: 'bool', nullable: false, default: false }) + isModerator: boolean +} diff --git a/database/entity/ContributionMessage.ts b/database/entity/ContributionMessage.ts index a5b370406..d1512863b 100644 --- a/database/entity/ContributionMessage.ts +++ b/database/entity/ContributionMessage.ts @@ -1 +1 @@ -export { ContributionMessage } from './0075-contribution_message_add_index/ContributionMessage' +export { ContributionMessage } from './0077-add_resubmission_date_contribution_message/ContributionMessage' diff --git a/database/migrations/0077-add_resubmission_date_contribution_message.ts b/database/migrations/0077-add_resubmission_date_contribution_message.ts new file mode 100644 index 000000000..598068b2f --- /dev/null +++ b/database/migrations/0077-add_resubmission_date_contribution_message.ts @@ -0,0 +1,12 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + `ALTER TABLE \`contribution_messages\` ADD COLUMN \`resubmission_at\` datetime NULL DEFAULT NULL AFTER \`deleted_by\`;`, + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(`ALTER TABLE \`contribution_messages\` DROP COLUMN \`resubmission_at\`;`) +} From ac7c4bf6861e58ceca7d013408b8c98129ba9c6c Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Mon, 20 Nov 2023 18:48:46 +0100 Subject: [PATCH 2/9] first approach, filtering out resubmission work, but expensive and query don't longer work --- .../ContributionMessagesFormular.vue | 38 +++++++++++++++ admin/src/components/input/TimePicker.vue | 48 +++++++++++++++++++ .../graphql/adminCreateContributionMessage.js | 8 +++- admin/src/graphql/adminListContributions.js | 2 + admin/src/locales/de.json | 3 ++ admin/src/locales/en.json | 3 ++ admin/src/pages/CreationConfirm.vue | 12 ++++- backend/src/config/index.ts | 2 +- .../graphql/arg/ContributionMessageArgs.ts | 5 ++ .../arg/SearchContributionsFilterArgs.ts | 4 ++ .../resolver/ContributionMessageResolver.ts | 5 +- .../resolver/util/findContributions.ts | 29 ++++++++++- dht-node/src/config/index.ts | 2 +- federation/src/config/index.ts | 2 +- 14 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 admin/src/components/input/TimePicker.vue diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue index 1e395c183..2799b74ad 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue @@ -4,6 +4,15 @@ + + + {{ $t('moderator.show-submission-form') }} + + + + + + import { adminCreateContributionMessage } from '@/graphql/adminCreateContributionMessage' import { adminUpdateContribution } from '@/graphql/adminUpdateContribution' +import TimePicker from '@/components/input/TimePicker' export default { + components: { + TimePicker, + }, name: 'ContributionMessagesFormular', props: { contributionId: { @@ -85,6 +98,9 @@ export default { memo: this.contributionMemo, }, loading: false, + resubmissionDate: null, + resubmissionTime: '00:00', + showResubmissionDate: false, chatOrMemo: 0, // 0 = Chat, 1 = Memo messageType: { DIALOG: 'DIALOG', @@ -93,6 +109,22 @@ export default { } }, methods: { + combineResubmissionDateAndTime() { + if (this.resubmissionDate) { + const formattedDate = new Date(this.resubmissionDate) + console.log('resubmission time: %s', this.resubmissionTime) + const [hours, minutes] = this.resubmissionTime.split(':') + console.log('hours: %s, minutes: %s', hours, minutes) + formattedDate.setHours(parseInt(hours)) + console.log('set hours: %d', formattedDate.getHours()) + formattedDate.setMinutes(parseInt(minutes)) + console.log('set minutes: %d', formattedDate.getMinutes()) + console.log('IOS String: %s', formattedDate.toISOString()) + return formattedDate.toString() + } else { + return null + } + }, onSubmit(mType) { this.loading = true if (this.chatOrMemo === 0) { @@ -103,6 +135,9 @@ export default { contributionId: this.contributionId, message: this.form.text, messageType: mType, + resubmissionAt: this.showResubmissionDate + ? this.combineResubmissionDateAndTime() + : null, }, }) .then((result) => { @@ -141,6 +176,9 @@ export default { onReset(event) { this.form.text = '' this.form.memo = this.contributionMemo + this.showResubmissionDate = false + this.resubmissionDate = null + this.resubmissionTime = '00:00' }, enableMemo() { this.chatOrMemo = 1 diff --git a/admin/src/components/input/TimePicker.vue b/admin/src/components/input/TimePicker.vue new file mode 100644 index 000000000..7a8111f1e --- /dev/null +++ b/admin/src/components/input/TimePicker.vue @@ -0,0 +1,48 @@ + + + diff --git a/admin/src/graphql/adminCreateContributionMessage.js b/admin/src/graphql/adminCreateContributionMessage.js index 66750b833..df7ca5458 100644 --- a/admin/src/graphql/adminCreateContributionMessage.js +++ b/admin/src/graphql/adminCreateContributionMessage.js @@ -1,11 +1,17 @@ import gql from 'graphql-tag' export const adminCreateContributionMessage = gql` - mutation ($contributionId: Int!, $message: String!, $messageType: ContributionMessageType) { + mutation ( + $contributionId: Int! + $message: String! + $messageType: ContributionMessageType + $resubmissionAt: String + ) { adminCreateContributionMessage( contributionId: $contributionId message: $message messageType: $messageType + resubmissionAt: $resubmissionAt ) { id message diff --git a/admin/src/graphql/adminListContributions.js b/admin/src/graphql/adminListContributions.js index e11ebfa05..5daa742b5 100644 --- a/admin/src/graphql/adminListContributions.js +++ b/admin/src/graphql/adminListContributions.js @@ -9,6 +9,7 @@ export const adminListContributions = gql` $userId: Int $query: String $noHashtag: Boolean + $hideResubmission: Boolean ) { adminListContributions( currentPage: $currentPage @@ -18,6 +19,7 @@ export const adminListContributions = gql` userId: $userId query: $query noHashtag: $noHashtag + hideResubmission: $hideResubmission ) { contributionCount contributionList { diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index 264029cc6..5b6fefc47 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -99,6 +99,8 @@ } }, "hide_details": "Details verbergen", + "hide_resubmission": "Wiedervorlage verbergen", + "hide_resubmission_tooltip": "Verbirgt alle Schöpfungen für die ein Moderator ein Erinnerungsdatum festgelegt hat.", "lastname": "Nachname", "math": { "equals": "=", @@ -111,6 +113,7 @@ "moderator": { "chat": "Chat", "history": "Die Daten wurden geändert. Dies sind die alten Daten.", + "show-submission-form": "warten auf Erinnerung?", "moderator": "Moderator", "notice": "Moderator Notiz", "memo": "Memo", diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index dbd831bb9..57d375c77 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -99,6 +99,8 @@ } }, "hide_details": "Hide details", + "hide_resubmission": "Hide resubmission", + "hide_resubmission_tooltip": "Hides all creations for which a moderator has set a reminder date.", "lastname": "Lastname", "math": { "equals": "=", @@ -111,6 +113,7 @@ "moderator": { "chat": "Chat", "history": "The data has been changed. This is the old data.", + "show-submission-form": "wait for reminder?", "moderator": "Moderator", "notice": "Moderator note", "memo": "Memo", diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue index 3ca382c43..1e6ff8231 100644 --- a/admin/src/pages/CreationConfirm.vue +++ b/admin/src/pages/CreationConfirm.vue @@ -2,10 +2,16 @@ @@ -176,6 +177,9 @@ export default { reloadContribution(id) { this.$emit('reload-contribution', id) }, + updateContributions() { + this.$emit('update-contributions') + }, }, } diff --git a/admin/src/pages/Overview.vue b/admin/src/pages/Overview.vue index 90924c9a9..f921d2bc3 100644 --- a/admin/src/pages/Overview.vue +++ b/admin/src/pages/Overview.vue @@ -49,6 +49,7 @@ export default { // may be at some point we need a pagination here return { statusFilter: this.statusFilter, + hideResubmission: true, } }, update({ adminListContributions }) { From 5d7a75603c55844173393460de9f1b0038f314da Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Thu, 23 Nov 2023 14:58:56 +0100 Subject: [PATCH 5/9] pass the status of the hideResubmission checkbox through to ContributionMessagesFormular --- .../ContributionMessagesFormular.vue | 10 +++++++++- .../ContributionMessages/ContributionMessagesList.vue | 5 +++++ admin/src/components/Tables/OpenCreationsTable.vue | 5 +++++ admin/src/pages/CreationConfirm.vue | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue index e00684a53..41f88b1af 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue @@ -90,6 +90,10 @@ export default { type: String, required: true, }, + hideResubmission: { + type: Boolean, + required: true, + }, }, data() { return { @@ -136,7 +140,11 @@ export default { }, }) .then((result) => { - if (this.showResubmissionDate && this.combineResubmissionDateAndTime() > new Date()) { + if ( + this.hideResubmission && + this.showResubmissionDate && + this.combineResubmissionDateAndTime() > new Date() + ) { this.$emit('update-contributions') } else { this.$emit('get-list-contribution-messages', this.contributionId) diff --git a/admin/src/components/ContributionMessages/ContributionMessagesList.vue b/admin/src/components/ContributionMessages/ContributionMessagesList.vue index 2b9c063bf..229fe6e04 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesList.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesList.vue @@ -12,6 +12,7 @@ Date: Thu, 23 Nov 2023 15:13:02 +0100 Subject: [PATCH 6/9] show hideResubmissionCheckbox only on page with pending contribution, else default false --- admin/src/pages/CreationConfirm.vue | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/admin/src/pages/CreationConfirm.vue b/admin/src/pages/CreationConfirm.vue index 6841579fc..a0cd9a8a3 100644 --- a/admin/src/pages/CreationConfirm.vue +++ b/admin/src/pages/CreationConfirm.vue @@ -6,8 +6,8 @@ {{ $t('no_hashtag') }}

-

- +

+ {{ $t('hide_resubmission') }} @@ -132,7 +132,7 @@ export default { pageSize: 25, query: '', noHashtag: null, - hideResubmission: true, + hideResubmissionModel: true, } }, watch: { @@ -433,6 +433,12 @@ export default { return 'info' } }, + showResubmissionCheckbox() { + return this.tabIndex === 0 + }, + hideResubmission() { + return this.showResubmissionCheckbox ? this.hideResubmissionModel : false + }, }, apollo: { ListAllContributions: { From 224b453af69a48e45d7df4238e525b2640e15642 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Thu, 23 Nov 2023 15:42:42 +0100 Subject: [PATCH 7/9] show updated from moderator for moderator history messages --- .../ContributionMessages/ContributionMessagesListItem.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue index 01f835197..60f0a0819 100644 --- a/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue +++ b/frontend/src/components/ContributionMessages/ContributionMessagesListItem.vue @@ -4,9 +4,12 @@ {{ $d(new Date(message.createdAt), 'short') }} -

+
{{ storeName.username }} {{ $t('contribution.isEdited') }}
+
+ {{ $t('community.moderator') }} {{ $t('contribution.isEdited') }} +
{{ $t('contribution.oldContribution') }}
From 2b77caa51e4ec1d6a0c491faf17ae7394a1b10c2 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Thu, 23 Nov 2023 15:59:36 +0100 Subject: [PATCH 8/9] fix backend linting --- backend/src/graphql/arg/ContributionMessageArgs.ts | 3 ++- backend/src/graphql/resolver/util/findContributions.ts | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/arg/ContributionMessageArgs.ts b/backend/src/graphql/arg/ContributionMessageArgs.ts index f79a596c8..e20686187 100644 --- a/backend/src/graphql/arg/ContributionMessageArgs.ts +++ b/backend/src/graphql/arg/ContributionMessageArgs.ts @@ -1,9 +1,10 @@ -import { isValidDateString } from '@/graphql/validator/DateString' import { IsInt, IsString, IsEnum } from 'class-validator' import { ArgsType, Field, Int, InputType } from 'type-graphql' import { ContributionMessageType } from '@enum/ContributionMessageType' +import { isValidDateString } from '@/graphql/validator/DateString' + @InputType() @ArgsType() export class ContributionMessageArgs { diff --git a/backend/src/graphql/resolver/util/findContributions.ts b/backend/src/graphql/resolver/util/findContributions.ts index 2853b7b96..9d26d212c 100644 --- a/backend/src/graphql/resolver/util/findContributions.ts +++ b/backend/src/graphql/resolver/util/findContributions.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-object-injection */ -import { Brackets, In, IsNull, LessThanOrEqual, Like, MoreThan, Not, SelectQueryBuilder } from '@dbTools/typeorm' -import { Contribution, Contribution as DbContribution } from '@entity/Contribution' +import { Brackets, In, Like, Not, SelectQueryBuilder } from '@dbTools/typeorm' +import { Contribution as DbContribution } from '@entity/Contribution' import { ContributionMessage } from '@entity/ContributionMessage' import { Paginated } from '@arg/Paginated' @@ -20,7 +20,6 @@ function joinRelationsRecursive( currentPath: string, ): void { for (const key in relations) { - // console.log('leftJoin: %s, %s', `${currentPath}.${key}`, key) queryBuilder.leftJoinAndSelect(`${currentPath}.${key}`, key) if (typeof relations[key] === 'object') { // If it's a nested relation From 4665d42405a1eb9a3650ecef1c27466021b7e0f3 Mon Sep 17 00:00:00 2001 From: einhorn_b Date: Thu, 23 Nov 2023 19:56:38 +0100 Subject: [PATCH 9/9] fix add add tests --- .../ContributionMessagesFormular.spec.js | 52 ++++++++++++++- .../ContributionMessagesFormular.vue | 17 +++-- .../ContributionMessagesList.spec.js | 11 ++++ .../Tables/OpenCreationsTable.spec.js | 1 + admin/src/components/input/TimePicker.spec.js | 63 +++++++++++++++++++ admin/src/components/input/TimePicker.vue | 4 +- admin/src/pages/CreationConfirm.spec.js | 9 +++ admin/src/pages/Overview.spec.js | 1 + 8 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 admin/src/components/input/TimePicker.spec.js diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js b/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js index f19459ce9..3638f5180 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.spec.js @@ -14,6 +14,7 @@ describe('ContributionMessagesFormular', () => { const propsData = { contributionId: 42, contributionMemo: 'It is a test memo', + hideResubmission: true, } const mocks = { @@ -95,13 +96,14 @@ describe('ContributionMessagesFormular', () => { await wrapper.find('button[data-test="submit-dialog"]').trigger('click') }) - it('moderatorMesage has `DIALOG`', () => { + it('moderatorMessage has `DIALOG`', () => { expect(apolloMutateMock).toBeCalledWith({ mutation: adminCreateContributionMessage, variables: { contributionId: 42, message: 'text form message', messageType: 'DIALOG', + resubmissionAt: null, }, }) }) @@ -128,6 +130,7 @@ describe('ContributionMessagesFormular', () => { contributionId: 42, message: 'text form message', messageType: 'MODERATOR', + resubmissionAt: null, }, }) }) @@ -137,6 +140,53 @@ describe('ContributionMessagesFormular', () => { }) }) + describe('send resubmission contribution message with success', () => { + const futureDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days in milliseconds + + beforeEach(async () => { + await wrapper.setData({ + form: { + text: 'text form message', + }, + showResubmissionDate: true, + resubmissionDate: futureDate, + resubmissionTime: '08:46', + }) + await wrapper.find('button[data-test="submit-moderator"]').trigger('click') + }) + + it('graphql payload contain resubmission date', () => { + const futureDateExactTime = futureDate + futureDateExactTime.setHours(8) + futureDateExactTime.setMinutes(46) + expect(apolloMutateMock).toBeCalledWith({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: 42, + message: 'text form message', + messageType: 'MODERATOR', + resubmissionAt: futureDateExactTime.toString(), + }, + }) + }) + + it('toasts an success message', () => { + expect(toastSuccessSpy).toBeCalledWith('message.request') + }) + }) + + describe('set memo', () => { + beforeEach(async () => { + await wrapper.setData({ + chatOrMemo: 0, + }) + await wrapper.find('button[data-test="submit-memo"]').trigger('click') + }) + it('check chatOrMemo value is 1', () => { + expect(wrapper.vm.chatOrMemo).toBe(1) + }) + }) + describe('update contribution memo from moderator for user created contributions', () => { beforeEach(async () => { await wrapper.setData({ diff --git a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue index 41f88b1af..9b27f34a8 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue +++ b/admin/src/components/ContributionMessages/ContributionMessagesFormular.vue @@ -114,15 +114,11 @@ export default { }, methods: { combineResubmissionDateAndTime() { - if (this.resubmissionDate) { - const formattedDate = new Date(this.resubmissionDate) - const [hours, minutes] = this.resubmissionTime.split(':') - formattedDate.setHours(parseInt(hours)) - formattedDate.setMinutes(parseInt(minutes)) - return formattedDate - } else { - return null - } + const formattedDate = new Date(this.resubmissionDate) + const [hours, minutes] = this.resubmissionTime.split(':') + formattedDate.setHours(parseInt(hours)) + formattedDate.setMinutes(parseInt(minutes)) + return formattedDate }, onSubmit(mType) { this.loading = true @@ -196,7 +192,8 @@ export default { return ( (this.chatOrMemo === 0 && this.form.text === '') || this.loading || - (this.chatOrMemo === 1 && this.form.memo.length < 5) + (this.chatOrMemo === 1 && this.form.memo.length < 5) || + (this.showResubmissionDate && !this.resubmissionDate) ) }, moderatorDisabled() { diff --git a/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js b/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js index b38c4e7d4..fe91abe6c 100644 --- a/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js +++ b/admin/src/components/ContributionMessages/ContributionMessagesList.spec.js @@ -89,6 +89,7 @@ describe('ContributionMessagesList', () => { contributionMemo: 'test memo', contributionUserId: 108, contributionStatus: 'PENDING', + hideResubmission: true, } const mocks = { @@ -155,5 +156,15 @@ describe('ContributionMessagesList', () => { expect(wrapper.emitted('reload-contribution')[0]).toEqual([3]) }) }) + + describe('test update-contributions', () => { + beforeEach(() => { + wrapper.vm.updateContributions() + }) + + it('emits update-contributions', () => { + expect(wrapper.emitted('update-contributions')).toBeTruthy() + }) + }) }) }) diff --git a/admin/src/components/Tables/OpenCreationsTable.spec.js b/admin/src/components/Tables/OpenCreationsTable.spec.js index 054ef9067..6babe9956 100644 --- a/admin/src/components/Tables/OpenCreationsTable.spec.js +++ b/admin/src/components/Tables/OpenCreationsTable.spec.js @@ -70,6 +70,7 @@ const propsData = { { key: 'confirm', label: 'save' }, ], toggleDetails: false, + hideResubmission: true, } const mocks = { diff --git a/admin/src/components/input/TimePicker.spec.js b/admin/src/components/input/TimePicker.spec.js new file mode 100644 index 000000000..82f51a969 --- /dev/null +++ b/admin/src/components/input/TimePicker.spec.js @@ -0,0 +1,63 @@ +import { mount } from '@vue/test-utils' +import TimePicker from './TimePicker.vue' + +describe('TimePicker', () => { + it('updates timeValue on input and emits input event', async () => { + const wrapper = mount(TimePicker, { + propsData: { + value: '12:34', // Set an initial value for testing + }, + }) + + const input = wrapper.find('input[type="text"]') + + // Simulate user input + await input.setValue('23:45') + + // Check if timeValue is updated + expect(wrapper.vm.timeValue).toBe('23:45') + + // Check if input event is emitted with updated value + expect(wrapper.emitted().input).toBeTruthy() + expect(wrapper.emitted().input[0]).toEqual(['23:45']) + }) + + it('validates and corrects time format on blur', async () => { + const wrapper = mount(TimePicker) + + const input = wrapper.find('input[type="text"]') + + // Simulate user input + await input.setValue('99:99') + expect(wrapper.emitted().input).toBeTruthy() + expect(wrapper.emitted().input[0]).toEqual(['99:99']) + + // Trigger blur event + await input.trigger('blur') + + // Check if timeValue is corrected to valid format + expect(wrapper.vm.timeValue).toBe('23:59') // Maximum allowed value for hours and minutes + + // Check if input event is emitted with corrected value + expect(wrapper.emitted().input).toBeTruthy() + expect(wrapper.emitted().input[1]).toEqual(['23:59']) + }) + + it('check handling of empty input', async () => { + const wrapper = mount(TimePicker) + const input = wrapper.find('input[type="text"]') + + // Simulate user input with non-numeric characters + await input.setValue('') + + // Trigger blur event + await input.trigger('blur') + + // Check if non-numeric characters are filtered out + expect(wrapper.vm.timeValue).toBe('00:00') + + // Check if input event is emitted with filtered value + expect(wrapper.emitted().input).toBeTruthy() + expect(wrapper.emitted().input[1]).toEqual(['00:00']) + }) +}) diff --git a/admin/src/components/input/TimePicker.vue b/admin/src/components/input/TimePicker.vue index 7a8111f1e..e8fb416ad 100644 --- a/admin/src/components/input/TimePicker.vue +++ b/admin/src/components/input/TimePicker.vue @@ -36,8 +36,8 @@ export default { let [hours, minutes] = this.timeValue.split(':') // Validate hours and minutes - hours = Math.min(Math.max(parseInt(hours) || 0, 0), 23) - minutes = Math.min(Math.max(parseInt(minutes) || 0, 0), 59) + hours = Math.min(parseInt(hours) || 0, 23) + minutes = Math.min(parseInt(minutes) || 0, 59) // Update the value with correct format this.timeValue = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}` diff --git a/admin/src/pages/CreationConfirm.spec.js b/admin/src/pages/CreationConfirm.spec.js index 4842c8b3b..cba169655 100644 --- a/admin/src/pages/CreationConfirm.spec.js +++ b/admin/src/pages/CreationConfirm.spec.js @@ -347,6 +347,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: false, noHashtag: null, order: 'DESC', pageSize: 25, @@ -364,6 +365,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: true, noHashtag: null, order: 'DESC', pageSize: 25, @@ -382,6 +384,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: false, noHashtag: null, order: 'DESC', pageSize: 25, @@ -400,6 +403,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: false, noHashtag: null, order: 'DESC', pageSize: 25, @@ -418,6 +422,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: false, noHashtag: null, order: 'DESC', pageSize: 25, @@ -440,6 +445,7 @@ describe('CreationConfirm', () => { it('calls the API again', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 2, + hideResubmission: false, noHashtag: null, order: 'DESC', pageSize: 25, @@ -457,6 +463,7 @@ describe('CreationConfirm', () => { it('refetches contributions with proper filter and current page = 1', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: true, noHashtag: null, order: 'DESC', pageSize: 25, @@ -480,6 +487,7 @@ describe('CreationConfirm', () => { it('calls the API with query', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: true, noHashtag: null, order: 'DESC', pageSize: 25, @@ -496,6 +504,7 @@ describe('CreationConfirm', () => { it('calls the API with empty query', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: true, noHashtag: null, order: 'DESC', pageSize: 25, diff --git a/admin/src/pages/Overview.spec.js b/admin/src/pages/Overview.spec.js index d5265f0e2..c0e358478 100644 --- a/admin/src/pages/Overview.spec.js +++ b/admin/src/pages/Overview.spec.js @@ -116,6 +116,7 @@ describe('Overview', () => { it('calls the adminListContributions query', () => { expect(adminListContributionsMock).toBeCalledWith({ currentPage: 1, + hideResubmission: true, order: 'DESC', pageSize: 25, statusFilter: ['IN_PROGRESS', 'PENDING'],