diff --git a/package-lock.json b/package-lock.json index 879d42f..66aeb5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,12 +18,13 @@ "axios": "^1.6.7", "bootstrap": "^5.3.2", "bootstrap-css": "^4.0.0-alpha.5", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", "formik": "^2.4.5", "json-schema-to-typescript": "^13.1.2", "jsonpath": "^1.1.1", "lodash": "^4.17.21", "markdown-it": "^14.1.0", - "moment": "^2.30.1", "react": "^18.3.1", "react-bootstrap": "^2.10.1", "react-countup": "^6.5.3", @@ -4333,15 +4334,24 @@ } }, "node_modules/date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/date-fns-tz": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz", + "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==", + "license": "MIT", + "peerDependencies": { + "date-fns": "^3.0.0 || ^4.0.0" + } + }, "node_modules/debug": { "version": "4.3.7", "license": "MIT", @@ -7204,13 +7214,6 @@ "ufo": "^1.5.3" } }, - "node_modules/moment": { - "version": "2.30.1", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.3", "license": "MIT" @@ -7946,6 +7949,16 @@ "react-dom": "^16.9.0 || ^17 || ^18" } }, + "node_modules/react-datepicker/node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/react-devtools-inline": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/react-devtools-inline/-/react-devtools-inline-4.4.0.tgz", diff --git a/package.json b/package.json index 7eb5bfa..9541868 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,13 @@ "axios": "^1.6.7", "bootstrap": "^5.3.2", "bootstrap-css": "^4.0.0-alpha.5", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", "formik": "^2.4.5", "json-schema-to-typescript": "^13.1.2", "jsonpath": "^1.1.1", "lodash": "^4.17.21", "markdown-it": "^14.1.0", - "moment": "^2.30.1", "react": "^18.3.1", "react-bootstrap": "^2.10.1", "react-countup": "^6.5.3", diff --git a/src/api/taxonomicService/GetTaxonomicService.ts b/src/api/taxonomicService/GetTaxonomicService.ts index c61d793..d48acd2 100644 --- a/src/api/taxonomicService/GetTaxonomicService.ts +++ b/src/api/taxonomicService/GetTaxonomicService.ts @@ -1,6 +1,6 @@ /* Import Dependencies */ import axios from 'axios'; -import moment from 'moment'; +import { format } from 'date-fns'; /* Import Types */ import { TaxonomicService, CordraResult } from 'app/Types'; @@ -39,8 +39,8 @@ const GetTaxonomicService = async ({ handle }: { handle?: string }) => { }; /* Set created and modified */ - taxonomicService.taxonomicService['schema:dateCreated'] = moment(new Date(data.attributes.metadata.createdOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); - taxonomicService.taxonomicService['schema:dateModified'] = moment(new Date(data.attributes.metadata.modifiedOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); + taxonomicService.taxonomicService['schema:dateCreated'] = format(new Date(data.attributes.metadata.createdOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); + taxonomicService.taxonomicService['schema:dateModified'] = format(new Date(data.attributes.metadata.modifiedOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); } catch (error) { console.error(error); diff --git a/src/api/taxonomicService/GetTaxonomicServices.ts b/src/api/taxonomicService/GetTaxonomicServices.ts index f5a9bd0..846ffb4 100644 --- a/src/api/taxonomicService/GetTaxonomicServices.ts +++ b/src/api/taxonomicService/GetTaxonomicServices.ts @@ -1,6 +1,6 @@ /* Import Dependencies */ import axios from 'axios'; -import moment from 'moment'; +import { format } from 'date-fns'; import { isEmpty } from 'lodash'; /* Import Types */ @@ -86,8 +86,8 @@ const GetTaxonomicServices = async ({ pageNumber, pageSize, searchFilters }: { p const taxonomicService = dataFragment.attributes.content as TaxonomicService; /* Set created and modified */ - taxonomicService.taxonomicService['schema:dateCreated'] = moment(new Date(dataFragment.attributes.metadata.createdOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); - taxonomicService.taxonomicService['schema:dateModified'] = moment(new Date(dataFragment.attributes.metadata.modifiedOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); + taxonomicService.taxonomicService['schema:dateCreated'] = format(new Date(dataFragment.attributes.metadata.createdOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); + taxonomicService.taxonomicService['schema:dateModified'] = format(new Date(dataFragment.attributes.metadata.modifiedOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); /* Push to taxonomic services array */ taxonomicServices.push(taxonomicService); diff --git a/src/api/taxonomicService/InsertTaxonomicService.ts b/src/api/taxonomicService/InsertTaxonomicService.ts index 2cfd0b8..747cfd7 100644 --- a/src/api/taxonomicService/InsertTaxonomicService.ts +++ b/src/api/taxonomicService/InsertTaxonomicService.ts @@ -1,6 +1,6 @@ /* Import Dependencies */ import axios from 'axios'; -import moment from 'moment'; +import { format } from 'date-fns'; /* Import Types */ import { TaxonomicService, CordraResult, Dict } from 'app/Types'; @@ -55,8 +55,8 @@ const InsertTaxonomicService = async ({ taxonomicServiceRecord }: { taxonomicSer taxonomicService = data.attributes.content as TaxonomicService; /* Set created and modified */ - taxonomicService.taxonomicService['schema:dateCreated'] = moment(new Date(data.attributes.metadata.createdOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); - taxonomicService.taxonomicService['schema:dateModified'] = moment(new Date(data.attributes.metadata.modifiedOn)).format('YYYY-MM-DDTHH:mm:ss.sssZ'); + taxonomicService.taxonomicService['schema:dateCreated'] = format(new Date(data.attributes.metadata.createdOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); + taxonomicService.taxonomicService['schema:dateModified'] = format(new Date(data.attributes.metadata.modifiedOn), "yyyy-MM-dd'T'HH:mm:ss.SSSxxx"); } catch (error) { console.error(error); diff --git a/src/components/search/components/SearchResult.tsx b/src/components/search/components/SearchResult.tsx index 6e01730..189457a 100644 --- a/src/components/search/components/SearchResult.tsx +++ b/src/components/search/components/SearchResult.tsx @@ -1,7 +1,7 @@ /* Import Dependencies */ import { useNavigate } from 'react-router-dom'; import classNames from 'classnames'; -import moment from 'moment'; +import { format } from 'date-fns'; import { Row, Col } from 'react-bootstrap'; /* Import Hooks */ @@ -72,7 +72,7 @@ const SearchResult = (props: Props) => { {(!previewImage || window.innerWidth < 768) &&

{taxonomicService.taxonomicService['schema:availableLanguage']?.join(' / ').toUpperCase()}

@@ -97,7 +97,9 @@ const SearchResult = (props: Props) => { {(!previewImage || window.innerWidth < 768) && -

{moment(taxonomicService.taxonomicService['schema:dateCreated']).format('MMM DD - YYYY')}

+

{taxonomicService.taxonomicService['schema:dateCreated'] && + format(taxonomicService.taxonomicService['schema:dateCreated'], 'MMMM dd - yyyy')} +

} @@ -123,7 +125,9 @@ const SearchResult = (props: Props) => { {/* Publishing Date */} -

{moment(taxonomicService.taxonomicService['schema:dateCreated']).format('MMM DD - YYYY')}

+

{taxonomicService.taxonomicService['schema:dateCreated'] && + format(taxonomicService.taxonomicService['schema:dateCreated'], 'MMMM dd - yyyy') + }

diff --git a/src/components/taxonomicService/TaxonomicService.tsx b/src/components/taxonomicService/TaxonomicService.tsx index e51ed33..3f63560 100644 --- a/src/components/taxonomicService/TaxonomicService.tsx +++ b/src/components/taxonomicService/TaxonomicService.tsx @@ -1,6 +1,6 @@ /* Import Dependencies */ import classNames from 'classnames'; -import moment from 'moment'; +import { format } from 'date-fns'; import { useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { Container, Row, Col } from 'react-bootstrap'; @@ -129,7 +129,7 @@ const TaxonomicService = () => { licence: taxonomicService.taxonomicService['schema:license'], sourceURL: taxonomicService.taxonomicService['schema:url'], changeLog: taxonomicService.taxonomicService['schema:about'], - datePublished: moment(taxonomicService.taxonomicService['schema:datePublished']).format('MMM DD - YYYY'), + datePublished: taxonomicService.taxonomicService['schema:datePublished'] && format(taxonomicService.taxonomicService['schema:datePublished'], 'MMMM dd - yyyy'), availableOnAppStore: taxonomicService.taxonomicService['schema:additionalProperty']?.[1] as string[] | undefined, documentationURL: taxonomicService.taxonomicService['schema:documentation'], paymentModel: taxonomicService.taxonomicService['schema:additionalProperty']?.[2] as string | undefined, diff --git a/src/components/taxonomicService/components/DescriptionBlock.tsx b/src/components/taxonomicService/components/DescriptionBlock.tsx index 5e334c0..d109d03 100644 --- a/src/components/taxonomicService/components/DescriptionBlock.tsx +++ b/src/components/taxonomicService/components/DescriptionBlock.tsx @@ -1,7 +1,7 @@ /* Import Dependencies */ import classNames from 'classnames'; +import { format } from 'date-fns'; import MarkdownIt from 'markdown-it'; -import moment from 'moment'; import { Row, Col } from 'react-bootstrap'; /* Import Types */ @@ -82,7 +82,9 @@ const DescriptionBlock = (props: Props) => { className="mt-2 mt-lg-0" >

Publishing date

-

{moment(taxonomicService.taxonomicService['schema:dateCreated']).format('MMM DD - YYYY')}

+

{taxonomicService.taxonomicService['schema:dateCreated'] && + format(taxonomicService.taxonomicService['schema:dateCreated'], 'MMMM dd - yyyy')} +

{/* Quality score */} { applicableToServiceTypes?: string[] } } = {}; + const inactiveFormSections: { + [section: string]: { + type: string, + jsonPath: string, + fields: FormField[], + applicableToServiceTypes?: string[] + } + } = {}; const initialFormValues: Dict = {}; /** @@ -105,14 +113,8 @@ const FormBuilder = (props: Props) => { }; /* Construct initial form values */ - Object.entries(formTemplate).forEach(([_key, formSection]) => { - if ((serviceType && formSection.applicableToServiceTypes?.includes(serviceType)) || !formSection.applicableToServiceTypes) { - formSections[formSection.title] = { - type: formSection.type, - jsonPath: formSection.jsonPath ?? '', - fields: [] - }; - + if (isEmpty(initialFormValues)) { + Object.entries(formTemplate).forEach(([_key, formSection]) => { /* If is array, push to initial form values */ if (formSection.type === 'array') { jp.value(initialFormValues, formSection.jsonPath ?? '', []); @@ -132,10 +134,36 @@ const FormBuilder = (props: Props) => { /* Add to initial form values */ jp.value(initialFormValues, field.jsonPath, DetermineInitialFormValue(field.type, field.const)); } + }); + }); + } + + /* Construct form sections */ + Object.entries(formTemplate).forEach(([_key, formSection]) => { + if ((serviceType && formSection.applicableToServiceTypes?.includes(serviceType)) || !formSection.applicableToServiceTypes || !serviceType) { + formSections[formSection.title] = { + type: formSection.type, + jsonPath: formSection.jsonPath ?? '', + fields: [], + applicableToServiceTypes: formSection.applicableToServiceTypes + }; + formSection.fields.forEach(field => { /* Push to form fields */ formSections[formSection.title].fields.push(field); }); + } else { + inactiveFormSections[formSection.title] = { + type: formSection.type, + jsonPath: formSection.jsonPath ?? '', + fields: [], + applicableToServiceTypes: formSection.applicableToServiceTypes + }; + + formSection.fields.forEach(field => { + /* Push to form fields */ + inactiveFormSections[formSection.title].fields.push(field); + }); } }); @@ -207,10 +235,21 @@ const FormBuilder = (props: Props) => { }; }; + /** + * Function to remove irrelevant classes from the form values object + * @param obj The form values object + */ + const CheckForIrrelevantClasses = (obj: Dict) => { + Object.keys(obj).forEach(key => { + if (Object.values(inactiveFormSections).find(values => values.jsonPath === `$['${key}']`)) { + delete obj[key]; + } + }); + }; + return (
{ await new Promise((resolve) => setTimeout(resolve, 100)); @@ -246,21 +285,27 @@ const FormBuilder = (props: Props) => { }; Object.values(formSections).forEach(formSection => { - formSection.fields.filter(field => field.required).forEach(field => { - if (field.jsonPath.includes('index')) { - const array = jp.value(values, field.jsonPath.split("['index']").at(0) as string); - - ValidateArray(array); - } else if (isEmpty(jp.value(values, field.jsonPath))) { - validationFlag = false; - } - }); + if ((formSection?.applicableToServiceTypes?.includes(values['schema:service']['schema:serviceType'])) || !formSection.applicableToServiceTypes) { + formSection.fields.filter(field => field.required).forEach(field => { + if (field.jsonPath.includes('index')) { + const array = jp.value(values, field.jsonPath.split("['index']").at(0) as string); + + ValidateArray(array); + } else if (isEmpty(jp.value(values, field.jsonPath))) { + validationFlag = false; + } + }); + } }); if (validationFlag && captchaHook.captchaStatus.solution !== null) { /* Start loading indication */ setLoading(true); + /** + * Function to search for and remove empty properties in the given form values object + * @param obj The form values object + */ const RemoveEmptyProperties = (obj: Dict) => { for (const key in obj) { if (isEmpty(obj[key]) || (Array.isArray(obj[key]) && !obj[key].find((value: string) => !!value))) { @@ -274,6 +319,7 @@ const FormBuilder = (props: Props) => { let taxonomicServiceRecord = cloneDeep(values); RemoveEmptyProperties(taxonomicServiceRecord); + CheckForIrrelevantClasses(taxonomicServiceRecord); try { await InsertTaxonomicService({ diff --git a/src/sources/forms/TaxonomicServiceForm.json b/src/sources/forms/TaxonomicServiceForm.json index de38473..7a4a41b 100644 --- a/src/sources/forms/TaxonomicServiceForm.json +++ b/src/sources/forms/TaxonomicServiceForm.json @@ -347,6 +347,16 @@ "type": "ror", "required": true } + ], + "applicableToServiceTypes": [ + "AI training dataset", + "e-Learning service", + "Factsheet", + "Identification tool", + "Knowledge website", + "Mobile app", + "Reference collection", + "Specimen dataset not in GBIF" ] }, "maintainer": { @@ -382,33 +392,38 @@ "type": "ror", "required": true } + ], + "applicableToServiceTypes": [ + "Data tool", + "Identification tool" ] }, "funding": { "title": "Funding", + "jsonPath": "$['schema:fundingScheme']", "type": "object", "fields": [ { - "jsonPath": "$['schema:fundingscheme']['schema:award']", + "jsonPath": "$['schema:fundingScheme']['schema:award']", "title": "Award", "description": "An award won by or for this service", "type": "string" }, { - "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:identifier']", + "jsonPath": "$['schema:fundingScheme']['schema:funding']['schema:identifier']", "title": "Grant Identifier", "description": "A unique identifier to identify the funder organisation; ROR identifiers are valid", "type": "string", "required": true }, { - "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:description']", + "jsonPath": "$['schema:fundingScheme']['schema:funding']['schema:description']", "title": "Grant Description", "description": "A description of the service's grant", "type": "string" }, { - "jsonPath": "$['schema:fundingscheme']['schema:funding']['schema:funder']", + "jsonPath": "$['schema:fundingScheme']['schema:funding']['schema:funder']", "title": "Funding Organisation", "description": "An organization that supports (sponsors) something through some kind of financial contribution", "type": "ror", @@ -416,11 +431,22 @@ } ], "applicableToServiceTypes": [ - "AI training dataset" + "AI training dataset", + "CrowdSourcing", + "Data tool", + "e-Learning service", + "Factsheet", + "Identification tool", + "Knowledge website", + "Mobile app", + "Service inventory", + "Reference collection", + "Specimen dataset not in GBIF" ] }, "softwareSourceCode": { "title": "Software Source Code", + "jsonPath": "$['schema:softwareSourceCode']", "type": "object", "fields": [ { @@ -462,7 +488,8 @@ } ], "applicableToServiceTypes": [ - "AI training dataset" + "Data tool", + "Identification tool" ] }, "associatedMedia": {