From 1efe3182e1573e8b9728cf6b76de8e31c4b4cf0e Mon Sep 17 00:00:00 2001 From: Riku Rouvila Date: Wed, 18 Dec 2024 16:18:41 +0200 Subject: [PATCH] Events v2: add config for summary and workqueues (#1188) * feat: add config for summary and workqueues * configure statusm created at, modified at to be default columns * feat: move review title to config * feat: add form for register * add conditional rules for action visibility * add more actions and conditionality * do not show register action after a record already has a register action * Events v2: form field validation example (#343) * add conditional rules for action visibility * add more actions and conditionality * do not show register action after a record already has a register action * add example form field validation * comment out file field for now * remove conditionals * update toolkit version * add e2e back * remove added schema file * upgrade faker.js --------- Co-authored-by: Markus Co-authored-by: jamil314 --- package.json | 4 +- src/data-generator/address.ts | 2 +- src/data-generator/users.ts | 2 +- src/form/tennis-club-membership.ts | 384 +++++++++++++++++++++-------- yarn.lock | 43 +++- 5 files changed, 321 insertions(+), 114 deletions(-) diff --git a/package.json b/package.json index 12b72df00..e9ed98521 100644 --- a/package.json +++ b/package.json @@ -71,12 +71,12 @@ "vitest": "^2.1.2" }, "dependencies": { - "@faker-js/faker": "^6.0.0-alpha.5", + "@faker-js/faker": "^7.6.0", "@hapi/boom": "^9.1.1", "@hapi/h2o2": "^9.1.0", "@hapi/hapi": "^20.0.1", "@hapi/inert": "^6.0.3", - "@opencrvs/toolkit": "^0.0.5", + "@opencrvs/toolkit": "0.0.6-events", "@types/chalk": "^2.2.0", "@types/code": "^4.0.3", "@types/csv2json": "^1.4.0", diff --git a/src/data-generator/address.ts b/src/data-generator/address.ts index 0ee4e915d..c5225e910 100644 --- a/src/data-generator/address.ts +++ b/src/data-generator/address.ts @@ -1,4 +1,4 @@ -import faker from '@faker-js/faker' +import { faker } from '@faker-js/faker' import { Location } from './location' export function createAddressInput(location: Location, type: string) { diff --git a/src/data-generator/users.ts b/src/data-generator/users.ts index 393fa8693..59c7b093f 100644 --- a/src/data-generator/users.ts +++ b/src/data-generator/users.ts @@ -1,4 +1,4 @@ -import faker from '@faker-js/faker' +import { faker } from '@faker-js/faker' import { log } from './util' import { Facility, getFacilities, Location } from './location' import fetch from 'node-fetch' diff --git a/src/form/tennis-club-membership.ts b/src/form/tennis-club-membership.ts index 7cd31b438..efb0ec55d 100644 --- a/src/form/tennis-club-membership.ts +++ b/src/form/tennis-club-membership.ts @@ -9,7 +9,142 @@ * Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. */ -import { defineConfig } from '@opencrvs/toolkit/events' +import { defineConfig, defineForm } from '@opencrvs/toolkit/events' +import { + defineConditional, + or, + eventHasAction, + userHasScope, + and, + not, + field +} from '@opencrvs/toolkit/conditionals' + +const TENNIS_CLUB_FORM = defineForm({ + label: { + id: 'event.tennis-club-membership.action.declare.form.label', + defaultMessage: 'Tennis club membership application', + description: 'This is what this form is referred as in the system' + }, + review: { + title: { + id: 'event.tennis-club-membership.action.declare.form.review.title', + defaultMessage: 'Member declaration for {firstname} {surname}', + description: 'Title of the form to show in review page' + } + }, + active: true, + version: { + id: '1.0.0', + label: { + id: 'event.tennis-club-membership.action.declare.form.version.1', + defaultMessage: 'Version 1', + description: 'This is the first version of the form' + } + }, + pages: [ + { + id: 'applicant', + title: { + id: 'event.tennis-club-membership.action.declare.form.section.who.title', + defaultMessage: 'Who is applying for the membership?', + description: 'This is the title of the section' + }, + fields: [ + { + id: 'applicant.firstname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Applicant's first name", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.firstname.label' + } + }, + { + id: 'applicant.surname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Applicant's surname", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.surname.label' + } + }, + { + id: 'applicant.dob', + type: 'DATE', + required: true, + validation: [ + { + message: { + defaultMessage: 'Please enter a valid date', + description: 'This is the error message for invalid date', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.dob.error' + }, + validator: field('applicant.dob').isBeforeNow() + } + ], + label: { + defaultMessage: "Applicant's date of birth", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.who.field.dob.label' + } + } + // { + // id: 'applicant.image', + // type: 'FILE', + // required: false, + // label: { + // defaultMessage: "Applicant's profile picture", + // description: 'This is the label for the field', + // id: 'event.tennis-club-membership.action.declare.form.section.who.field.image.label' + // } + // } + ] + }, + { + id: 'recommender', + title: { + id: 'event.tennis-club-membership.action.declare.form.section.recommender.title', + defaultMessage: 'Who is recommending the applicant?', + description: 'This is the title of the section' + }, + fields: [ + { + id: 'recommender.firstname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's first name", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.firstname.label' + } + }, + { + id: 'recommender.surname', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's surname", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.surname.label' + } + }, + { + id: 'recommender.id', + type: 'TEXT', + required: true, + label: { + defaultMessage: "Recommender's membership ID", + description: 'This is the label for the field', + id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.id.label' + } + } + ] + } + ] +}) export const tennisClubMembershipEvent = defineConfig({ id: 'TENNIS_CLUB_MEMBERSHIP', @@ -18,115 +153,160 @@ export const tennisClubMembershipEvent = defineConfig({ description: 'This is what this event is referred as in the system', id: 'event.tennis-club-membership.label' }, + summary: { + title: { + defaultMessage: '{applicant.firstname} {applicant.surname}', + description: 'This is the title of the summary', + id: 'event.tennis-club-membership.summary.title' + }, + fields: [ + { + id: 'applicant.firstname' + }, + { + id: 'applicant.surname' + }, + { + id: 'recommender.firstname' + }, + { + id: 'recommender.surname' + }, + { + id: 'recommender.id' + } + ] + }, + workqueues: [ + { + id: 'all', + title: { + defaultMessage: 'All events', + description: 'Label for in progress workqueue', + id: 'event.tennis-club-membership.workqueue.all.label' + }, + fields: [ + { + id: 'applicant.firstname' + }, + { + id: 'applicant.surname' + } + ], + filters: [] + }, + { + id: 'ready-for-review', + title: { + defaultMessage: 'Ready for review', + description: 'Label for in review workqueue', + id: 'event.tennis-club-membership.workqueue.in-review.label' + }, + fields: [ + { + id: 'applicant.firstname' + }, + { + id: 'event.type' + }, + { + id: 'event.createdAt' + }, + { + id: 'event.modifiedAt' + } + ], + filters: [ + { + status: ['DECLARED'] + } + ] + }, + { + id: 'registered', + title: { + defaultMessage: 'Ready to print', + description: 'Label for registered workqueue', + id: 'event.tennis-club-membership.workqueue.registered.label' + }, + fields: [ + { + id: 'applicant.firstname' + }, + { + id: 'event.type' + }, + { + id: 'event.createdAt' + }, + { + id: 'event.modifiedAt' + } + ], + filters: [ + { + status: ['REGISTERED'] + } + ] + } + ], actions: [ { type: 'DECLARE', label: { - defaultMessage: 'Send an application', + defaultMessage: 'Declare', description: 'This is shown as the action name anywhere the user can trigger the action from', id: 'event.tennis-club-membership.action.declare.label' }, - forms: [ - { - label: { - id: 'event.tennis-club-membership.action.declare.form.label', - defaultMessage: 'Tennis club membership application', - description: 'This is what this form is referred as in the system' - }, - active: true, - version: { - id: '1.0.0', - label: { - id: 'event.tennis-club-membership.action.declare.form.version.1', - defaultMessage: 'Version 1', - description: 'This is the first version of the form' - } - }, - pages: [ - { - id: 'applicant', - title: { - id: 'event.tennis-club-membership.action.declare.form.section.who.title', - defaultMessage: 'Who is applying for the membership?', - description: 'This is the title of the section' - }, - fields: [ - { - id: 'applicant.firstname', - type: 'TEXT', - required: true, - label: { - defaultMessage: "Applicant's first name", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.who.field.firstname.label' - } - }, - { - id: 'applicant.surname', - type: 'TEXT', - required: true, - label: { - defaultMessage: "Applicant's surname", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.who.field.surname.label' - } - }, - { - id: 'applicant.dob', - type: 'DATE', - required: true, - label: { - defaultMessage: "Applicant's date of birth", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.who.field.dob.label' - } - } - ] - }, - { - id: 'recommender', - title: { - id: 'event.tennis-club-membership.action.declare.form.section.recommender.title', - defaultMessage: 'Who is recommending the applicant?', - description: 'This is the title of the section' - }, - fields: [ - { - id: 'recommender.firstname', - type: 'TEXT', - required: true, - label: { - defaultMessage: "Recommender's first name", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.firstname.label' - } - }, - { - id: 'recommender.surname', - type: 'TEXT', - required: true, - label: { - defaultMessage: "Recommender's surname", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.surname.label' - } - }, - { - id: 'recommender.id', - type: 'TEXT', - required: true, - label: { - defaultMessage: "Recommender's membership ID", - description: 'This is the label for the field', - id: 'event.tennis-club-membership.action.declare.form.section.recommender.field.id.label' - } - } - ] - } - ] - } - ] + forms: [TENNIS_CLUB_FORM], + allowedWhen: defineConditional(not(eventHasAction('DECLARE'))) + }, + { + type: 'VALIDATE', + label: { + defaultMessage: 'Validate', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.tennis-club-membership.action.validate.label' + }, + allowedWhen: defineConditional(eventHasAction('DECLARE')), + forms: [TENNIS_CLUB_FORM] + }, + { + type: 'REGISTER', + label: { + defaultMessage: 'Register', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.tennis-club-membership.action.register.label' + }, + allowedWhen: defineConditional( + and( + or( + eventHasAction('VALIDATE'), + and(eventHasAction('DECLARE'), userHasScope('register')) + ), + not(eventHasAction('REGISTER')) + ) + ), + forms: [TENNIS_CLUB_FORM] + }, + { + type: 'CUSTOM', + label: { + defaultMessage: 'My custom action', + description: + 'This is shown as the action name anywhere the user can trigger the action from', + id: 'event.tennis-club-membership.action.sdf.label' + }, + allowedWhen: defineConditional( + or( + eventHasAction('VALIDATE'), + and(eventHasAction('DECLARE'), userHasScope('register')) + ) + ), + forms: [] } ] }) diff --git a/yarn.lock b/yarn.lock index 564b51c43..39488e3e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -732,10 +732,10 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== -"@faker-js/faker@^6.0.0-alpha.5": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-6.3.1.tgz#1ae963dd40405450a2945408cba553e1afa3e0fb" - integrity sha512-8YXBE2ZcU/pImVOHX7MWrSR/X5up7t6rPWZlk34RwZEcdr3ua6X+32pSd6XuOQRN+vbuvYNfA6iey8NbrjuMFQ== +"@faker-js/faker@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.6.0.tgz#9ea331766084288634a9247fcd8b84f16ff4ba07" + integrity sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw== "@formatjs/ecma402-abstract@2.2.3": version "2.2.3" @@ -1712,10 +1712,12 @@ dependencies: "@octokit/openapi-types" "^18.0.0" -"@opencrvs/toolkit@^0.0.5": - version "0.0.5" - resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.5.tgz#dbd3395b561c1eb3527b8cfc28b6e1dd301671f3" - integrity sha512-Ex45aFUJ1iQwpYGMHhxVbhpuQFqVlmdSkNXz1phkM7KAzNtngL2KGIJCc6TFv4GKOHU/lLEfmGyrhIDCKlA7fQ== +"@opencrvs/toolkit@0.0.6-events": + version "0.0.6-events" + resolved "https://registry.yarnpkg.com/@opencrvs/toolkit/-/toolkit-0.0.6-events.tgz#48327b3e322bc82ae60ec13fc855e2e23300b298" + integrity sha512-wh4qaPJktG4Dnh+zFFbDv5b/oZ+bTSzQYRfOzLGS1KNqG7h2j6iZfvmSBtyL0rb/SrpIHcI+9eoqLfwg8cjfyQ== + dependencies: + ajv "^8.17.1" "@parcel/watcher-android-arm64@2.5.0": version "2.5.0" @@ -2585,6 +2587,16 @@ ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -4091,6 +4103,11 @@ fast-safe-stringify@^2.1.1: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + fast-url-parser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" @@ -5183,6 +5200,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -6651,6 +6673,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"