From 85dbec82be9da5641d2473b69069d04cbb90fc12 Mon Sep 17 00:00:00 2001 From: valdeza Date: Fri, 16 Feb 2024 21:11:11 +0000 Subject: [PATCH 01/22] improve(draft): display dwc:associatedOccurrences in record pages NOTE: Contains draft code. Make the following corrections before use in production: /public/client/js/lib/dwc_fields.js:: - "dwc:associatedOccurrences" is duplicated and will appear twice in specimen record pages. - Value for $.names.extendedspecimen has an inappropriate value. /public/client/js/react/src/record.js:: - New fields are given CSS property 'backgroundColor: palegreen' for visibility. Find and remove all usages of 'DEMO_BGCOLOR'. - If not giving 'extendedspecimen' its own section, remove from array assigned to `var sorder` I had dwc:associatedOccurrences in two places since I was not really sure where we want to put it. ref: redmine#6277 (Feature: "Implement extended specimen record fields") --- public/client/js/lib/dwc_fields.js | 8 +++++++- public/client/js/react/src/record.js | 10 ++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/public/client/js/lib/dwc_fields.js b/public/client/js/lib/dwc_fields.js index fea573fa..a537463f 100644 --- a/public/client/js/lib/dwc_fields.js +++ b/public/client/js/lib/dwc_fields.js @@ -21,6 +21,7 @@ module.exports = { "dwc:taxonRemarks" ], "specimen": [ + "dwc:associatedOccurrences", "dwc:typeStatus", "dwc:identifiedBy", "dwc:dateIdentified", @@ -123,6 +124,9 @@ module.exports = { "dwc:lowestBiostratigraphicZone", "dwc:lithostratigraphicTerms" ], + "extendedspecimen": [ + "dwc:associatedOccurrences" + ], "other": [ "dcterms:language", "dcterms:modified", @@ -476,6 +480,8 @@ module.exports = { "idigbio:institutionName": "Institution Name", "idigbio:collectionCategory": "Collection Category", "idigbio:collectionSize": "Collection Size", - "idigbio:importantHoldings": "Important Holdings" + "idigbio:importantHoldings": "Important Holdings", + "extendedspecimen": "Links DEMOLOCATION 01", + "dwc:associatedOccurrences": "Associated Occurrences" } } \ No newline at end of file diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 1cbb00b8..c790ec8d 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -8,7 +8,9 @@ import fields from '../../lib/fields'; import dqFlags from '../../lib/dq_flags'; import idbapi from '../../lib/idbapi'; - +const + NO_DEMO_BGCOLOR = false, + DEMO_BGCOLOR = 'palegreen'; const Row = ({keyid, data}) => { @@ -20,7 +22,7 @@ const Row = ({keyid, data}) => { }); return ( - + {name} @@ -44,7 +46,7 @@ const Section = ({name, data, active}) => { cl="section"; } return ( -
+
{dwc.names[name]}
{rows} @@ -114,7 +116,7 @@ const Record = ({record, raw }) => { var has = []; var non_props_record = [] - var sorder = ['taxonomy','specimen','collectionevent','locality','paleocontext','other']; + var sorder = ['extendedspecimen','taxonomy','specimen','collectionevent','locality','paleocontext','other']; var tabs = [], self = this var cnt = 0; From cda67242c2b61eab09e3a75d18defec5002b8462 Mon Sep 17 00:00:00 2001 From: valdeza Date: Tue, 27 Feb 2024 16:06:44 +0000 Subject: [PATCH 02/22] draft: improve: display dwc:Identification in record pages NOTE: Contains draft code. See /DRAFT-UNRESOLVED.txt for list of corrections to perform before use in production. Unlike other fields, dwc:Identification seems to be returned from the search API as an array, which made it require special handling when creating the appropriate sections and table rows. I imagined the group of sections looking better, but now that I see it, I should probably move it to its own tab. Also added some typing documentation from when I was trying to figure out how the search API gets processed for record page rendering. ref: redmine#6277 (Feature: "Implement extended specimen record fields") --- DRAFT-UNRESOLVED.txt | 12 +++ public/client/js/lib/dwc_fields.js | 10 ++- public/client/js/react/src/record.js | 128 +++++++++++++++++++++++---- 3 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 DRAFT-UNRESOLVED.txt diff --git a/DRAFT-UNRESOLVED.txt b/DRAFT-UNRESOLVED.txt new file mode 100644 index 00000000..48e90291 --- /dev/null +++ b/DRAFT-UNRESOLVED.txt @@ -0,0 +1,12 @@ +(Remove this file when resolved) + +Make the following corrections before use in production: +/public/client/js/lib/dwc_fields.js:: +- "dwc:associatedOccurrences" is duplicated and will appear twice in + specimen record pages. +- Value for $.names.extendedspecimen has an inappropriate value. +/public/client/js/react/src/record.js:: +- New fields are given CSS property 'backgroundColor: palegreen' + for visibility. Find and remove all usages of 'DEMO_BGCOLOR'. +- If not giving 'extendedspecimen' its own section, remove from array + assigned to `var sorder` diff --git a/public/client/js/lib/dwc_fields.js b/public/client/js/lib/dwc_fields.js index a537463f..236b86f3 100644 --- a/public/client/js/lib/dwc_fields.js +++ b/public/client/js/lib/dwc_fields.js @@ -127,6 +127,9 @@ module.exports = { "extendedspecimen": [ "dwc:associatedOccurrences" ], + "idhistory": [ + "dwc:Identification" + ], "other": [ "dcterms:language", "dcterms:modified", @@ -482,6 +485,11 @@ module.exports = { "idigbio:collectionSize": "Collection Size", "idigbio:importantHoldings": "Important Holdings", "extendedspecimen": "Links DEMOLOCATION 01", - "dwc:associatedOccurrences": "Associated Occurrences" + "dwc:associatedOccurrences": "Associated Occurrences", + "idhistory": "Identification History", + "dwc:identificationID": "Identification ID", + "dwc:verbatimIdentification": "Verbatim Identification", + "dwc:identifiedByID": "Identified By (ID)", + "dwc:identificationVerificationStatus": "Identification Verification Status" } } \ No newline at end of file diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index c790ec8d..1e51104d 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -30,25 +30,46 @@ const Row = ({keyid, data}) => { }; +/** + * @param {object} props + * @param {string} props.name Section ID name, corresponding to dwc_fields.js. + * If 'idhistory:', {@link data} is handled differently. + * @param {object|object[]} props.data Field key-values specific to this section. + * If {@link name} is 'idhistory:', this will instead be expected to be an array. + * @param {boolean} props.active If `true`, assign CSS class to allow this section to be visible. + */ const Section = ({name, data, active}) => { var rows = [] var data = data; - _.each(data,function(fld){ - var key = Object.keys(fld)[0]; - if(_.isString(fld[key])){ - rows.push(); - } - }); + if(name.startsWith('idhistory')){ + _.forIn(data,function(fieldValue, fieldKey){ + if(_.isString(fieldValue)){ + rows.push(); + } + }) + }else{ + _.each(data,function(fld){ + var key = Object.keys(fld)[0]; + if(_.isString(fld[key])){ + rows.push(); + } + }); + } var cl = "section visible-print-block"; if(active){ cl="section"; } + let isDemoContent = !NO_DEMO_BGCOLOR && (['extendedspecimen'].includes(name) || name.startsWith('idhistory')); + /** @type {string} */ + let sectionName = (name.startsWith('idhistory') && name.includes(':') + ? dwc.names['idhistory'] +' '+ name.split(':')[1] + : dwc.names[name]); return ( -
-
{dwc.names[name]}
-
+
+
{sectionName}
+
{rows}
@@ -79,6 +100,11 @@ const Flags = ({flags, active}) => { }; +/** + * @param {object} props + * @param {object} props.record {@linkcode props.raw}, after processing through dwc_fields.js + * @param {object} props.raw Raw search API JSON response for a record + */ const Record = ({record, raw }) => { const [active, setActive] = useState("record") const [nonPropsRecord, setNonPropsRecord] = useState([]) @@ -116,8 +142,7 @@ const Record = ({record, raw }) => { var has = []; var non_props_record = [] - var sorder = ['extendedspecimen','taxonomy','specimen','collectionevent','locality','paleocontext','other']; - var tabs = [], self = this + var sorder = ['extendedspecimen','taxonomy','specimen','collectionevent','locality','paleocontext','idhistory','other']; var cnt = 0; sorder.forEach(function(sec,index){ @@ -126,8 +151,17 @@ const Record = ({record, raw }) => { if(cnt===0){ active=true; } - //tabs.push() - non_props_record.push(
); + if(sec==='idhistory'){ + if(!Array.isArray(record[sec])){ + console.error('error creating section \'idhistory\': not an array'); + }else{ + record[sec].forEach((iden,iiden)=>{ + non_props_record.push(
); + }); + } + }else{ + non_props_record.push(
); + } cnt++; } }); @@ -248,6 +282,10 @@ const Citation = ({data, pubname}) => { }; +/** + * @param {object} props + * @param {object} props.record Raw search API JSON response for a record from /view/records/:id + */ const RecordPage = ({ record }) => { const navList = () => { const map = record.indexTerms.geopoint ?
  • Map
  • : null; @@ -324,17 +362,71 @@ const RecordPage = ({ record }) => { _.defaults(canonical, data); _.each(dwc.order, function (val, key) { - _.each(dwc.order[key], function (fld) { + if (key === 'idhistory') { + /* Requires special handling to flatten: + * Unlike other sections, this one is an array. + * + * We SHOULD get: + * localRecord: { + * idhistory: [{ + * idfield1_1: ..., + * idfield1_2: ..., + * ... + * },{ + * idfield2_1: ..., + * idfield2_2: ..., + * ... + * }, + * ... + * ] + * } + * Otherwise, with the original implementation under 'else', + * we end up with: + * localRecord: { + * idhistory: [{ + * 'dwc:Identification': [{ + * idfield1_1: ..., + * idfield1_2: ..., + * ... + * },{ + * idfield2_1: ..., + * idfield2_2: ..., + * ... + * }, + * ... + * ] + * }] + * } + */ + const fld = 'dwc:Identification'; if (_.has(canonical, fld)) { if (!_.has(localRecord, key)) { localRecord[key] = []; } - const datum = {}; - datum[fld] = canonical[fld]; - localRecord[key].push(datum); + if (!_.isArray(canonical[fld])) + console.error('error parsing field \'%s\': expected an array', fld); + _.forEach(canonical[fld], function (iden) { + let datum = {}; + _.forIn(iden, function(idenFieldValue, idenFieldName) { + datum[idenFieldName] = idenFieldValue; + }); + localRecord[key].push(datum); + }) has.push(fld); } - }); + } else { + _.each(dwc.order[key], function (fld) { + if (_.has(canonical, fld)) { + if (!_.has(localRecord, key)) { + localRecord[key] = []; + } + let datum = {}; + datum[fld] = canonical[fld]; + localRecord[key].push(datum); + has.push(fld); + } + }); + } }); const dif = _.difference(Object.keys(canonical), has); From e86ac99899efb32b0e9a53f681ff02d6a92cb0e9 Mon Sep 17 00:00:00 2001 From: valdeza Date: Wed, 28 Feb 2024 16:39:53 +0000 Subject: [PATCH 03/22] draft: improve: add 'ID History' record tab for dwc:Identification NOTE: Contains draft code. /public/client/less/record.less:: If not putting identification history in separate tab, #idhistory can be removed from this file and CSS stylesheets may be regenerated by running gulp task 'buildLess' /public/client/js/react/src/record.js:: dwc:Identification currently appears twice in specimen record pages: 1. Dedicated ID History tab 2. Added sections near bottom of Data tab (see current draft code notes at /DRAFT-UNRESOLVED.txt) Yep, seems to look better in its own tab. ref: redmine#6277 (Feature: "Implement extended specimen record fields") --- DRAFT-UNRESOLVED.txt | 10 +++++++++ public/client/js/react/src/record.js | 33 ++++++++++++++++++++++++---- public/client/less/record.less | 2 +- public/css/record.css | 10 ++++++--- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/DRAFT-UNRESOLVED.txt b/DRAFT-UNRESOLVED.txt index 48e90291..2bade62e 100644 --- a/DRAFT-UNRESOLVED.txt +++ b/DRAFT-UNRESOLVED.txt @@ -1,6 +1,10 @@ (Remove this file when resolved) Make the following corrections before use in production: +/public/client/less/record.less:: +- If not putting identification history in separate tab in record page, + remove #idhistory and run gulp task 'buildLess' to regenerate CSS stylesheets + (depends: /public/css/record.css) /public/client/js/lib/dwc_fields.js:: - "dwc:associatedOccurrences" is duplicated and will appear twice in specimen record pages. @@ -10,3 +14,9 @@ Make the following corrections before use in production: for visibility. Find and remove all usages of 'DEMO_BGCOLOR'. - If not giving 'extendedspecimen' its own section, remove from array assigned to `var sorder` +- "dwc:Identification" is duplicated and will appear twice in + specimen record pages: + 1. Dedicated ID History tab + 2. Added sections near bottom of Data tab +/public/css/record.css:: + This file is automatically generated. See /public/client/less/record.less diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 1e51104d..3e3efd48 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -108,6 +108,7 @@ const Flags = ({flags, active}) => { const Record = ({record, raw }) => { const [active, setActive] = useState("record") const [nonPropsRecord, setNonPropsRecord] = useState([]) + const [recordIdHistory, setRecordIdHistory] = useState([]) function formatJSON(json){ if (typeof json != 'string') { @@ -141,10 +142,12 @@ const Record = ({record, raw }) => { useEffect(() => { var has = []; - var non_props_record = [] + var non_props_record = []; + var record_id_history = []; var sorder = ['extendedspecimen','taxonomy','specimen','collectionevent','locality','paleocontext','idhistory','other']; var cnt = 0; + // Record tab rendering sorder.forEach(function(sec,index){ if(_.has(record,sec)){ var active=true; @@ -152,6 +155,7 @@ const Record = ({record, raw }) => { active=true; } if(sec==='idhistory'){ + //FIXME Implementation duplicated below (at "Render Identification History into its own tab") if(!Array.isArray(record[sec])){ console.error('error creating section \'idhistory\': not an array'); }else{ @@ -167,21 +171,42 @@ const Record = ({record, raw }) => { }); setNonPropsRecord([...nonPropsRecord, non_props_record]) - + // Render Identification History into its own tab, if applicable. + // + // Basically duplicate code from the 'idhistory' carve-out for Record tab rendering above + // (while indecisive about where we want to put this) + let sec = 'idhistory'; + if(_.has(record,sec)){ + var active=true; + if(!Array.isArray(record[sec])){ + console.error('error creating section \'idhistory\': not an array'); + }else{ + record[sec].forEach((iden,iiden)=>{ + record_id_history.push(
    ); + }); + } + } + setRecordIdHistory([...recordIdHistory, record_id_history]) }, []); + let + doRenderIdHistory = !!raw.indexTerms?.indexData?.['dwc:Identification'], + doRenderFlags = !!raw.indexTerms.flags; + return (
    • Data
    • - {raw.indexTerms.flags ?
    • Flags
    • : ''} + {doRenderIdHistory ?
    • ID History
    • : ''} + {doRenderFlags ?
    • Flags
    • : ''}
    • Raw
    {nonPropsRecord}
    - {raw.indexTerms.flags ? : ''} + {doRenderIdHistory ?
    {recordIdHistory}
    : ''} + {doRenderFlags ? : ''}

    diff --git a/public/client/less/record.less b/public/client/less/record.less index 5a3f2190..20a7a7e5 100644 --- a/public/client/less/record.less +++ b/public/client/less/record.less @@ -108,7 +108,7 @@ h1#banner{ #data{ margin-top:30px; - #record, #flags{ + #record, #flags, #idhistory{ border-top:@brd; font-size:13px; padding-top:20px; diff --git a/public/css/record.css b/public/css/record.css index 4040a367..cfa2ed48 100644 --- a/public/css/record.css +++ b/public/css/record.css @@ -252,20 +252,24 @@ h1#banner { margin-top: 30px; } #data #record, -#data #flags { +#data #flags, +#data #idhistory { border-top: 1px solid #ccc; font-size: 13px; padding-top: 20px; } #data #record .field-name, -#data #flags .field-name { +#data #flags .field-name, +#data #idhistory .field-name { font-weight: normal; color: #555; } #data #record .field-value, #data #flags .field-value, +#data #idhistory .field-value, #data #record .field-value a, -#data #flags .field-value a { +#data #flags .field-value a, +#data #idhistory .field-value a { word-wrap: break-all; word-break: break-word; overflow: hidden; From cd909d30fa2dc5ce628341a7abf2146a81f97231 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Fri, 12 Apr 2024 16:38:30 -0400 Subject: [PATCH 04/22] improve: use antd horizontal scroll table for 'idhistory' and 'extendedmeasurementorfact' --- Dockerfile | 7 +- package.json | 5 +- public/client/js/lib/dwc_fields.js | 4 + public/client/js/react/src/record.js | 114 +++-- public/client/less/record.less | 26 ++ yarn.lock | 662 +++++++++++++++++++++++++-- 6 files changed, 749 insertions(+), 69 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8a737021..585549ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,14 @@ -FROM ubuntu:18.04 +FROM ubuntu:20.04 ENV NODE_OPTIONS=--use-openssl-ca +ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update -RUN apt-get -y install make gcc g++ python bash git curl openssl nodejs npm +RUN apt-get -y install make gcc g++ python3 bash git curl openssl nodejs npm RUN npm i -g yarn RUN npm install -g n -RUN n 8.12.0 +RUN n 20.11.0 WORKDIR /var/www ADD . . diff --git a/package.json b/package.json index a0ba19b6..1f729c56 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@bower_components/leaflet-utfgrid": "danzel/Leaflet.utfgrid", "@bower_components/leaflet.fullscreen": "brunob/leaflet.fullscreen", "@elastic/filesaver": "^1.1.2", + "antd": "^5.16.1", "async": "^2.0.0", "body-parser": "^1.16.1", "bootstrap": "^3.3.6", @@ -46,7 +47,6 @@ "geopoint": "^1.0.1", "griddle-react": "^0.8.1", "hamljs": "^0.6.2", - "hiredis": "^0.5.0", "http-proxy": "~1.8.1", "jquery": "~3.3.1", "jquery-ui": "^1.12.1", @@ -120,6 +120,9 @@ "vinyl-source-stream": "^1.1.0", "watchify": "^3.7.0" }, + "resolutions": { + "graceful-fs": "^4.2.11" + }, "keywords": [], "repository": "git://github.com/idigbio/idigbio-portal", "license": "LGPL-3.0", diff --git a/public/client/js/lib/dwc_fields.js b/public/client/js/lib/dwc_fields.js index 236b86f3..05535f7c 100644 --- a/public/client/js/lib/dwc_fields.js +++ b/public/client/js/lib/dwc_fields.js @@ -130,6 +130,9 @@ module.exports = { "idhistory": [ "dwc:Identification" ], + "extendedmeasurementorfact": [ + "obis:ExtendedMeasurementOrFact" + ], "other": [ "dcterms:language", "dcterms:modified", @@ -487,6 +490,7 @@ module.exports = { "extendedspecimen": "Links DEMOLOCATION 01", "dwc:associatedOccurrences": "Associated Occurrences", "idhistory": "Identification History", + "extendedmeasurementorfact": "Extended Measurement or Fact", "dwc:identificationID": "Identification ID", "dwc:verbatimIdentification": "Verbatim Identification", "dwc:identifiedByID": "Identified By (ID)", diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 3e3efd48..629ad0c8 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -7,6 +7,7 @@ import moment from 'moment'; import fields from '../../lib/fields'; import dqFlags from '../../lib/dq_flags'; import idbapi from '../../lib/idbapi'; +import { Table } from 'antd'; const NO_DEMO_BGCOLOR = false, @@ -139,31 +140,82 @@ const Record = ({record, raw }) => { setActive(e.target.attributes['data-tab'].value) } + function extractKeys(arr) { // extracts keys from array of objects + return arr.reduce((keys, obj) => { + Object.keys(obj).forEach(key => { + if (!keys.includes(key)) { + keys.push(key); + } + }); + return keys; + }, []) + } + + function getAntdColumns(keys) { // takes a list of keys and formats them to be used as antd column headers + const sorted_keys = keys.sort((a, b) => { + if (a === 'dwc:scientificName') return -1; + if (b === 'dwc:scientificName') return 1; + return 0; + }) + return sorted_keys.map(key => ({ + title: _.isUndefined(dwc.names[key]) ? key : dwc.names[key], + dataIndex: key, + key: key, + })); + } + + function completeData(data, keys) { // instantiates missing keys to '' + return data.map((item, index) => { + keys.forEach(key => { + if (!item.hasOwnProperty(key)) { + item[key] = ''; + } + }); + return { ...item, key: index } + }); + } + + function getAntdTable(recordSection, sec) { + const allKeys = extractKeys(recordSection) + const columns = getAntdColumns(allKeys) + const rows = completeData(recordSection, allKeys) + return (
    +
    {dwc.names[sec]}
    + index % 2 === 0 ? 'evenRow' : 'oddRow'} + columns={columns} + dataSource={rows} + scroll={{x: 'max-content'}} + pagination={false} + size={"small"} + /> + ) + } + useEffect(() => { - var has = []; - var non_props_record = []; - var record_id_history = []; - var sorder = ['extendedspecimen','taxonomy','specimen','collectionevent','locality','paleocontext','idhistory','other']; - var cnt = 0; - - // Record tab rendering - sorder.forEach(function(sec,index){ - if(_.has(record,sec)){ - var active=true; - if(cnt===0){ - active=true; - } - if(sec==='idhistory'){ - //FIXME Implementation duplicated below (at "Render Identification History into its own tab") + var has = []; + var non_props_record = []; + var record_id_history = []; + var sorder = ['extendedspecimen', 'taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', 'idhistory', 'extendedmeasurementorfact', 'other']; + var cnt = 0; + // Record tab rendering + sorder.forEach(function (sec, index) { + if (_.has(record, sec)) { + var active = true; + if (cnt === 0) { + active = true; + } + if (sec === 'idhistory' || sec === 'extendedmeasurementorfact') { + //FIXME Implementation duplicated below (at "Render Identification History into its own tab") if(!Array.isArray(record[sec])){ console.error('error creating section \'idhistory\': not an array'); }else{ - record[sec].forEach((iden,iiden)=>{ - non_props_record.push(
    ); - }); + non_props_record.push(getAntdTable(record[sec], sec)) } - }else{ + } + else{ non_props_record.push(
    ); } cnt++; @@ -181,9 +233,7 @@ const Record = ({record, raw }) => { if(!Array.isArray(record[sec])){ console.error('error creating section \'idhistory\': not an array'); }else{ - record[sec].forEach((iden,iiden)=>{ - record_id_history.push(
    ); - }); + record_id_history = getAntdTable(record[sec], sec) } } setRecordIdHistory([...recordIdHistory, record_id_history]) @@ -197,17 +247,17 @@ const Record = ({record, raw }) => {
      -
    • Data
    • - {doRenderIdHistory ?
    • ID History
    • : ''} - {doRenderFlags ?
    • Flags
    • : ''} -
    • Raw
    • +
    • Data
    • + {doRenderIdHistory ?
    • ID History
    • : ''} + {doRenderFlags ?
    • Flags
    • : ''} +
    • Raw
    -
    +
    {nonPropsRecord}
    - {doRenderIdHistory ?
    {recordIdHistory}
    : ''} - {doRenderFlags ? : ''} -
    + {doRenderIdHistory ?
    {recordIdHistory}
    : ''} + {doRenderFlags ? : ''} +

    @@ -387,7 +437,7 @@ const RecordPage = ({ record }) => { _.defaults(canonical, data); _.each(dwc.order, function (val, key) { - if (key === 'idhistory') { + if (key === 'idhistory' || key === 'extendedmeasurementorfact') { /* Requires special handling to flatten: * Unlike other sections, this one is an array. * @@ -423,7 +473,7 @@ const RecordPage = ({ record }) => { * }] * } */ - const fld = 'dwc:Identification'; + const fld = key === 'idhistory' ? 'dwc:Identification' : 'obis:ExtendedMeasurementOrFact' ; if (_.has(canonical, fld)) { if (!_.has(localRecord, key)) { localRecord[key] = []; diff --git a/public/client/less/record.less b/public/client/less/record.less index 20a7a7e5..5549e815 100644 --- a/public/client/less/record.less +++ b/public/client/less/record.less @@ -105,6 +105,32 @@ h1#banner{ } } } +//.customTable .ant-table { +// background-color: #f0f2f5; /* Light grey background */ +//} +.custom-antd-table .ant-table-tbody > tr.oddRow > td { + background-color: #f9f9f9; /* Light grey background for odd rows */ + font-size: 13px; +} + +.custom-antd-table .ant-table-tbody > tr.evenRow > td { + background-color: #ffffff; /* White background for even rows */ + font-size: 13px; +} + +.custom-antd-table .ant-table-thead > tr > th { + font-size: 13px; + border-radius: 0; +} + +.custom-antd-table .ant-table { + border: 1px solid #ddd; /* Solid gray border */ +} + + +.ant-table { + border-radius: 0 !important; +} #data{ margin-top:30px; diff --git a/yarn.lock b/yarn.lock index 9f519d25..51e7a1f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,53 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.2.tgz#c5c753a467ce8d86ba7ca4736d2c01f599bb5492" + integrity sha512-7KJkhTiPiLHSu+LmMJnehfJ6242OCxSlR3xHVBecYxnMW8MS/878NXct1GqYARyL59fyeFdKRxXTfvR9SnDgJg== + dependencies: + "@ctrl/tinycolor" "^3.6.1" + +"@ant-design/cssinjs@^1.18.5": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.19.1.tgz#d3bb4f58ee884c9c757688e611e7a6de9867ea75" + integrity sha512-hgQ3wiys3X0sqDKWkqCJ6EYdF79i9JCvtavmIGwuuPUKmoJXV8Ff0sY+yQQSxk2dRmMyam/bYKo/Bwor45hnZw== + dependencies: + "@babel/runtime" "^7.11.1" + "@emotion/hash" "^0.8.0" + "@emotion/unitless" "^0.7.5" + classnames "^2.3.1" + csstype "^3.1.3" + rc-util "^5.35.0" + stylis "^4.0.13" + +"@ant-design/icons-svg@^4.4.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" + integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== + +"@ant-design/icons@^5.3.6": + version "5.3.6" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.3.6.tgz#172bbcfcfa39f9a1d9ef73ad5cf98c6184fb83f8" + integrity sha512-JeWsgNjvkTTC73YDPgWOgdScRku/iHN9JU0qk39OSEmJSCiRghQMLlxGTCY5ovbRRoXjxHXnUKgQEgBDnQfKmA== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.4.0" + "@babel/runtime" "^7.11.2" + classnames "^2.2.6" + rc-util "^5.31.1" + +"@ant-design/react-slick@~1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" + integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== + dependencies: + "@babel/runtime" "^7.10.4" + classnames "^2.2.5" + json2mq "^0.2.0" + resize-observer-polyfill "^1.5.1" + throttle-debounce "^5.0.0" + "@babel/cli@^7.0.0": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.23.4.tgz#f5cc90487278065fa0c3b1267cf0c1d44ddf85a7" @@ -998,6 +1045,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd" + integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.4.0": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -1052,11 +1106,26 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@ctrl/tinycolor@^3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== + "@elastic/filesaver@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@elastic/filesaver/-/filesaver-1.1.2.tgz#1998ffb3cd89c9da4ec12a7793bfcae10e30c77a" integrity sha512-YZbSufYFBhAj+S2cJgiKALoxIJevqXN2MSr6Yqr42rJdaPuM31cj6pUDwflkql1oDjupqD9la+MfxPFjXI1JFQ== +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + "@gulp-sourcemaps/identity-map@1.X": version "1.0.2" resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz#1e6fe5d8027b1f285dc0d31762f566bccd73d5a9" @@ -1261,6 +1330,72 @@ resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== +"@rc-component/color-picker@~1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.5.3.tgz#f3b0e14bb67ec5ee77d1fd5d261f63dd4fd00449" + integrity sha512-+tGGH3nLmYXTalVe0L8hSZNs73VTP5ueSHwUlDC77KKRaN7G4DS4wcpG5DTDzdcV/Yas+rzA6UGgIyzd8fS4cw== + dependencies: + "@babel/runtime" "^7.23.6" + "@ctrl/tinycolor" "^3.6.1" + classnames "^2.2.6" + rc-util "^5.38.1" + +"@rc-component/context@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" + integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== + dependencies: + "@babel/runtime" "^7.10.1" + rc-util "^5.27.0" + +"@rc-component/mini-decimal@^1.0.1": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" + integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== + dependencies: + "@babel/runtime" "^7.18.0" + +"@rc-component/mutate-observer@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" + integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/tour@~1.14.2": + version "1.14.2" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.14.2.tgz#142f0973975eb0e3af3c75aa868ffc9d8ba27cae" + integrity sha512-A75DZ8LVvahBIvxooj3Gvf2sxe+CGOkmzPNX7ek0i0AJHyKZ1HXe5ieIGo3m0FMdZfVOlbCJ952Duq8VKAHk6g== + dependencies: + "@babel/runtime" "^7.18.0" + "@rc-component/portal" "^1.0.0-9" + "@rc-component/trigger" "^2.0.0" + classnames "^2.3.2" + rc-util "^5.24.4" + +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.1.1.tgz#47973f1156ba63810c913eb46cbaedeba913874b" + integrity sha512-UjHkedkgtEcgQu87w1VuWug1idoDJV7VUt0swxHXRcmei2uu1AuUzGBPEUlmOmXGJ+YtTgZfVLi7kuAUKoZTMA== + dependencies: + "@babel/runtime" "^7.23.2" + "@rc-component/portal" "^1.1.0" + classnames "^2.3.2" + rc-motion "^2.0.0" + rc-resize-observer "^1.3.1" + rc-util "^5.38.0" + "@types/babel__core@^7.1.0": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" @@ -1616,6 +1751,60 @@ ansistyles@~0.1.3: resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539" integrity sha512-6QWEyvMgIXX0eO972y7YPBLSBsq7UWKFAoNNTLGaOJ9bstcEL9sCbcjf96dVfNDdUsRoGOK82vWFJlKApXds7g== +antd@^5.16.1: + version "5.16.1" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.16.1.tgz#a258e73cac8c6e63c4ec44588e5474f0403cd33a" + integrity sha512-XAlLRrgYV+nj9FHnkXEPS6HNcKcluEa8v44e7Cixjlp8aOXRhUI6IfZaKpc2MPGjQ+06rp62/dsxOUNJW9kfLA== + dependencies: + "@ant-design/colors" "^7.0.2" + "@ant-design/cssinjs" "^1.18.5" + "@ant-design/icons" "^5.3.6" + "@ant-design/react-slick" "~1.1.2" + "@babel/runtime" "^7.24.4" + "@ctrl/tinycolor" "^3.6.1" + "@rc-component/color-picker" "~1.5.3" + "@rc-component/mutate-observer" "^1.1.0" + "@rc-component/tour" "~1.14.2" + "@rc-component/trigger" "^2.0.0" + classnames "^2.5.1" + copy-to-clipboard "^3.3.3" + dayjs "^1.11.10" + qrcode.react "^3.1.0" + rc-cascader "~3.24.0" + rc-checkbox "~3.2.0" + rc-collapse "~3.7.3" + rc-dialog "~9.4.0" + rc-drawer "~7.1.0" + rc-dropdown "~4.2.0" + rc-field-form "~1.44.0" + rc-image "~7.6.0" + rc-input "~1.4.5" + rc-input-number "~9.0.0" + rc-mentions "~2.11.1" + rc-menu "~9.13.0" + rc-motion "^2.9.0" + rc-notification "~5.4.0" + rc-pagination "~4.0.4" + rc-picker "~4.3.0" + rc-progress "~4.0.0" + rc-rate "~2.12.0" + rc-resize-observer "^1.4.0" + rc-segmented "~2.3.0" + rc-select "~14.13.0" + rc-slider "~10.5.0" + rc-steps "~6.0.1" + rc-switch "~4.1.0" + rc-table "~7.45.4" + rc-tabs "~14.1.1" + rc-textarea "~1.6.3" + rc-tooltip "~6.2.0" + rc-tree "~5.8.5" + rc-tree-select "~5.19.0" + rc-upload "~4.5.2" + rc-util "^5.39.1" + scroll-into-view-if-needed "^3.1.0" + throttle-debounce "^5.0.0" + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1756,6 +1945,11 @@ array-slice@^1.0.0: resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== +array-tree-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" + integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== + array-uniq@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -1905,6 +2099,11 @@ async-some@~1.0.2: dependencies: dezalgo "^1.0.2" +async-validator@^4.1.0: + version "4.2.5" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" + integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== + async@^2.0.0, async@^2.0.1, async@^2.6.4: version "2.6.4" resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" @@ -2108,7 +2307,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.2.1, bindings@^1.5.0: +bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== @@ -2769,6 +2968,11 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-css@^4.1.5: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -2961,6 +3165,11 @@ compression@^1.6.2: safe-buffer "5.1.2" vary "~1.1.2" +compute-scroll-into-view@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" + integrity sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3093,6 +3302,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== +copy-to-clipboard@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + core-js-compat@^3.31.0, core-js-compat@^3.33.1: version "3.34.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.34.0.tgz#61a4931a13c52f8f08d924522bba65f8c94a5f17" @@ -3225,6 +3441,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + csurf@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/csurf/-/csurf-1.11.0.tgz#ab0c3c6634634192bd3d6f4b861be20800eeb61a" @@ -3298,6 +3519,11 @@ dateformat@^2.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" integrity sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw== +dayjs@^1.11.10: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + debug-fabulous@1.X: version "1.1.0" resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" @@ -5095,28 +5321,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@4.X, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: +graceful-fs@4.X, graceful-fs@^3.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.11, graceful-fs@~1.2.0, graceful-fs@~4.1.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graceful-fs@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.12.tgz#0034947ce9ed695ec8ab0b854bc919e82b1ffaef" - integrity sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg== - dependencies: - natives "^1.1.3" - -graceful-fs@~1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" - integrity sha512-iiTUZ5vZ+2ZV+h71XAgwCSu6+NAizhFU3Yw8aC/hH5SQ3SnISqEqAek40imAFGtDcwJKNhXvSY+hzIolnLwcdQ== - -graceful-fs@~4.1.6: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== - griddle-react@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/griddle-react/-/griddle-react-0.8.2.tgz#5bfe00b6680c571229916557c2f48ebc0798217a" @@ -5423,14 +5632,6 @@ hawk@~3.1.3: resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== -hiredis@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/hiredis/-/hiredis-0.5.0.tgz#db03a98becd7003d13c260043aceecfacdf59b87" - integrity sha512-fKf2yLN2hTiXWGPiJUhDFyBQ5arMb500QfF+xEodoMK/G+WxYU+ckDoMIjHK7+IDWCex1QfJz3QVCfsu557XgQ== - dependencies: - bindings "^1.2.1" - nan "^2.3.4" - hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -6683,6 +6884,13 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json2mq@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" + integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== + dependencies: + string-convert "^0.2.0" + json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -7567,7 +7775,7 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.3.4: +nan@^2.12.1: version "2.18.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== @@ -7594,11 +7802,6 @@ napi-macros@~2.0.0: resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== -natives@^1.1.3: - version "1.1.6" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.6.tgz#a603b4a498ab77173612b9ea1acdec4d980f00bb" - integrity sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -8757,6 +8960,11 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +qrcode.react@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" + integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== + qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -8855,6 +9063,357 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +rc-cascader@~3.24.0: + version "3.24.1" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.24.1.tgz#603bac4427f8865b6ec056705bae8485c65d7798" + integrity sha512-RgKuYgEGPx+6wCgguYFHjMsDZdCyydZd58YJRCfYQ8FObqLnZW0x/vUcEyPjhWIj1EhjV958IcR+NFPDbbj9kg== + dependencies: + "@babel/runtime" "^7.12.5" + array-tree-filter "^2.1.0" + classnames "^2.3.1" + rc-select "~14.13.0" + rc-tree "~5.8.1" + rc-util "^5.37.0" + +rc-checkbox@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.2.0.tgz#04f83b7f2bf63eaf0e2da25e947b88a0a6321b5a" + integrity sha512-8inzw4y9dAhZmv/Ydl59Qdy5tdp9CKg4oPVcRigi+ga/yKPZS5m5SyyQPtYSgbcqHRYOdUhiPSeKfktc76du1A== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.3.2" + rc-util "^5.25.2" + +rc-collapse@~3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.3.tgz#68161683d8fd1004bef4eb281fc106f3c8dc16eb" + integrity sha512-60FJcdTRn0X5sELF18TANwtVi7FtModq649H11mYF1jh83DniMoM4MqY627sEKRCTm4+WXfGDcB7hY5oW6xhyw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.3.4" + rc-util "^5.27.0" + +rc-dialog@~9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.4.0.tgz#194c107d34cb36a56f1db4a49dc73f6d59eeae85" + integrity sha512-AScCexaLACvf8KZRqCPz12BJ8olszXOS4lKlkMyzDQHS1m0zj1KZMYgmMCh39ee0Dcv8kyrj8mTqxuLyhH+QuQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.0.0-8" + classnames "^2.2.6" + rc-motion "^2.3.0" + rc-util "^5.21.0" + +rc-drawer@~7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.1.0.tgz#2beabb8bab1784aea255d0d850bc206c3dc715da" + integrity sha512-nBE1rF5iZvpavoyqhSSz2mk/yANltA7g3aF0U45xkx381n3we/RKs9cJfNKp9mSWCedOKWt9FLEwZDaAaOGn2w== + dependencies: + "@babel/runtime" "^7.23.9" + "@rc-component/portal" "^1.1.1" + classnames "^2.2.6" + rc-motion "^2.6.1" + rc-util "^5.38.1" + +rc-dropdown@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.0.tgz#c6052fcfe9c701487b141e411cdc277dc7c6f061" + integrity sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng== + dependencies: + "@babel/runtime" "^7.18.3" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.6" + rc-util "^5.17.0" + +rc-field-form@~1.44.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.44.0.tgz#a66548790fbcee8c5432e9f2efcd1b46b090984b" + integrity sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA== + dependencies: + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" + rc-util "^5.32.2" + +rc-image@~7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.6.0.tgz#2867087b77c8595ea9eb37d18ca863e47904b191" + integrity sha512-tL3Rvd1sS+frZQ01i+tkeUPaOeFz2iG9/scAt/Cfs0hyCRVA/w0Pu1J/JxIX8blalvmHE0bZQRYdOmRAzWu4Hg== + dependencies: + "@babel/runtime" "^7.11.2" + "@rc-component/portal" "^1.0.2" + classnames "^2.2.6" + rc-dialog "~9.4.0" + rc-motion "^2.6.2" + rc-util "^5.34.1" + +rc-input-number@~9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.0.0.tgz#47da6eaf95b0cf566fcdf68196c4dc82b8c8f166" + integrity sha512-RfcDBDdWFFetouWFXBA+WPEC8LzBXyngr9b+yTLVIygfFu7HiLRGn/s/v9wwno94X7KFvnb28FNynMGj9XJlDQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/mini-decimal" "^1.0.1" + classnames "^2.2.5" + rc-input "~1.4.0" + rc-util "^5.28.0" + +rc-input@~1.4.0, rc-input@~1.4.5: + version "1.4.5" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.4.5.tgz#1f2f13fb1adb15fce5936aebcfab354f41b4f348" + integrity sha512-AjzykhwnwYTRSwwgCu70CGKBIAv6bP2nqnFptnNTprph/TF1BAs0Qxl91mie/BR6n827WIJB6ZjaRf9iiMwAfw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.18.1" + +rc-mentions@~2.11.1: + version "2.11.1" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.11.1.tgz#3e742d4b558965701021cff75b84af6cd4744ed3" + integrity sha512-upb4AK1SRFql7qGnbLEvJqLMugVVIyjmwBJW9L0eLoN9po4JmJZaBzmKA4089fNtsU8k6l/tdZiVafyooeKnLw== + dependencies: + "@babel/runtime" "^7.22.5" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.6" + rc-input "~1.4.0" + rc-menu "~9.13.0" + rc-textarea "~1.6.1" + rc-util "^5.34.1" + +rc-menu@~9.13.0: + version "9.13.0" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.13.0.tgz#55426531af5fd0a2a0e0a50020cbd002b231edf9" + integrity sha512-1l8ooCB3HcYJKCltC/s7OxRKRjgymdl9htrCeGZcXNaMct0RxZRK6OPV3lPhVksIvAGMgzPd54ClpZ5J4b8cZA== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.0.0" + classnames "2.x" + rc-motion "^2.4.3" + rc-overflow "^1.3.1" + rc-util "^5.27.0" + +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" + integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.21.0" + +rc-notification@~5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.4.0.tgz#c5ea20bfe4ed2dbacc7ef945777626c050945db8" + integrity sha512-li19y9RoYJciF3WRFvD+DvWS70jdL8Fr+Gfb/OshK+iY6iTkwzoigmSIp76/kWh5tF5i/i9im12X3nsF85GYdA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.9.0" + rc-util "^5.20.1" + +rc-overflow@^1.3.1, rc-overflow@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" + integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-resize-observer "^1.0.0" + rc-util "^5.37.0" + +rc-pagination@~4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.0.4.tgz#ea401388ae86eac17ed5b41212d487f12b65b773" + integrity sha512-GGrLT4NgG6wgJpT/hHIpL9nELv27A1XbSZzECIuQBQTVSf4xGKxWr6I/jhpRPauYEWEbWVw22ObG6tJQqwJqWQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.3.2" + rc-util "^5.38.0" + +rc-picker@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.3.2.tgz#b62e02122b3b6043a5a347674e5d003b8e51873e" + integrity sha512-2NtobLxG2YqllXn4YczbupgIH6PSqzjCfFCnGlgPIY9k0HZti8WmBPjS1OD9JKQl+Tdg0pMVUeTEc07y4X9ZRQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.0.0" + classnames "^2.2.1" + rc-overflow "^1.3.2" + rc-resize-observer "^1.4.0" + rc-util "^5.38.1" + +rc-progress@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" + integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.6" + rc-util "^5.16.1" + +rc-rate@~2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.12.0.tgz#0182deffed3b009cdcc61660da8746c39ed91ed5" + integrity sha512-g092v5iZCdVzbjdn28FzvWebK2IutoVoiTeqoLTj9WM7SjA/gOJIw5/JFZMRyJYYVe1jLAU2UhAfstIpCNRozg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.0.1" + +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.38.0" + resize-observer-polyfill "^1.5.1" + +rc-segmented@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.3.0.tgz#b3fe080fb434a266c02e30bb62a47d2c6e094341" + integrity sha512-I3FtM5Smua/ESXutFfb8gJ8ZPcvFR+qUgeeGFQHBOvRiRKyAk4aBE5nfqrxXx+h8/vn60DQjOt6i4RNtrbOobg== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-select@~14.13.0: + version "14.13.1" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.13.1.tgz#2d3ca539032bf8184e511597bb93436e5b9cf02c" + integrity sha512-A1VHqjIOemxLnUGRxLGVqXBs8jGcJemI5NXxOJwU5PQc1wigAu1T4PRLgMkTPDOz8gPhlY9dwsPzMgakMc2QjQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^2.1.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-overflow "^1.3.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.2" + +rc-slider@~10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.5.0.tgz#1bd4853d114cb3403b67c485125887adb6a2a117" + integrity sha512-xiYght50cvoODZYI43v3Ylsqiw14+D7ELsgzR40boDZaya1HFa1Etnv9MDkQE8X/UrXAffwv2AcNAhslgYuDTw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.5" + rc-util "^5.27.0" + +rc-steps@~6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" + integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== + dependencies: + "@babel/runtime" "^7.16.7" + classnames "^2.2.3" + rc-util "^5.16.1" + +rc-switch@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" + integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== + dependencies: + "@babel/runtime" "^7.21.0" + classnames "^2.2.1" + rc-util "^5.30.0" + +rc-table@~7.45.4: + version "7.45.4" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.45.4.tgz#8657450d836f691e94ff2282a1c8ee344fe551e6" + integrity sha512-6aSbGrnkN2GLSt3s1x+wa4f3j/VEgg1uKPpaLY5qHH1/nFyreS2V7DFJ0TfUb18allf2FQl7oVYEjTixlBXEyQ== + dependencies: + "@babel/runtime" "^7.10.1" + "@rc-component/context" "^1.4.0" + classnames "^2.2.5" + rc-resize-observer "^1.1.0" + rc-util "^5.37.0" + rc-virtual-list "^3.11.1" + +rc-tabs@~14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-14.1.1.tgz#f49467feb69af0156c7864d04c21396e4c5c7911" + integrity sha512-5nOr9PVpJy2SWHTLgv1+kESDOb0tFzl0cYU9r9d8LfL0Wg9i/n1B558rmkxdQHgBwMqxmwoyPSAbQROxMQe8nw== + dependencies: + "@babel/runtime" "^7.11.2" + classnames "2.x" + rc-dropdown "~4.2.0" + rc-menu "~9.13.0" + rc-motion "^2.6.2" + rc-resize-observer "^1.0.0" + rc-util "^5.34.1" + +rc-textarea@~1.6.1, rc-textarea@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.6.3.tgz#3f294fdf5dfadbe9d0e7b695cea4da557728a9be" + integrity sha512-8k7+8Y2GJ/cQLiClFMg8kUXOOdvcFQrnGeSchOvI2ZMIVvX5a3zQpLxoODL0HTrvU63fPkRmMuqaEcOF9dQemA== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.1" + rc-input "~1.4.0" + rc-resize-observer "^1.0.0" + rc-util "^5.27.0" + +rc-tooltip@~6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.0.tgz#4dd7575674137a5b14f118a5c16435d3f5e4a9c9" + integrity sha512-iS/3iOAvtDh9GIx1ulY7EFUXUtktFccNLsARo3NPgLf0QW9oT0w3dA9cYWlhqAKmD+uriEwdWz1kH0Qs4zk2Aw== + dependencies: + "@babel/runtime" "^7.11.2" + "@rc-component/trigger" "^2.0.0" + classnames "^2.3.1" + +rc-tree-select@~5.19.0: + version "5.19.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.19.0.tgz#1da83694ffeeeed7ba5e4bd49a1d7df48501677d" + integrity sha512-f4l5EsmSGF3ggj76YTzKNPY9SnXfFaer7ZccTSGb3urUf54L+cCqyT+UsPr+S5TAr8mZSxJ7g3CgkCe+cVQ6sw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-select "~14.13.0" + rc-tree "~5.8.1" + rc-util "^5.16.1" + +rc-tree@~5.8.1, rc-tree@~5.8.5: + version "5.8.5" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.5.tgz#f714a383be27bd87366cf32f7f85b2af1fbae6b6" + integrity sha512-PRfcZtVDNkR7oh26RuNe1hpw11c1wfgzwmPFL0lnxGnYefe9lDAO6cg5wJKIAwyXFVt5zHgpjYmaz0CPy1ZtKg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.1" + +rc-upload@~4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.5.2.tgz#ea493fbaaf57d9369ee954b20e1d8bc35c818a1a" + integrity sha512-QO3ne77DwnAPKFn0bA5qJM81QBjQi0e0NHdkvpFyY73Bea2NfITiotqJqVjHgeYPOJu5lLVR32TNGP084aSoXA== + dependencies: + "@babel/runtime" "^7.18.3" + classnames "^2.2.5" + rc-util "^5.2.0" + +rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.28.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.39.1: + version "5.39.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.39.1.tgz#7bca4fb55e20add0eef5c23166cd9f9e5f51a8a1" + integrity sha512-OW/ERynNDgNr4y0oiFmtes3rbEamXw7GHGbkbNd9iRr7kgT03T6fT0b9WpJ3mbxKhyOcAHnGcIoh5u/cjrC2OQ== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: + version "3.11.4" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.4.tgz#d0a8937843160b7b00d5586854290bf56d396af7" + integrity sha512-NbBi0fvyIu26gP69nQBiWgUMTPX3mr4FcuBQiVqagU0BnuX8WQkiivnMs105JROeuUIFczLrlgUhLQwTWV1XDA== + dependencies: + "@babel/runtime" "^7.20.0" + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.36.0" + react-addons-css-transition-group@^15.5: version "15.6.2" resolved "https://registry.yarnpkg.com/react-addons-css-transition-group/-/react-addons-css-transition-group-15.6.2.tgz#9e4376bcf40b5217d14ec68553081cee4b08a6d6" @@ -8900,6 +9459,11 @@ react-is@^16.13.1, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-is@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-onclickoutside@^6.5.0: version "6.13.0" resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz#e165ea4e5157f3da94f4376a3ab3e22a565f4ffc" @@ -9426,6 +9990,11 @@ reselect@^3.0.1: resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" integrity sha512-b/6tFZCmRhtBMa4xGqiiRp9jh9Aqi2A687Lo265cN0/QohJQEBPiQ52f4QB6i0eF3yp3hmLL21LSGBcML2dlxA== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -9650,6 +10219,13 @@ scheduler@^0.20.2: loose-envify "^1.1.0" object-assign "^4.1.1" +scroll-into-view-if-needed@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" + integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== + dependencies: + compute-scroll-into-view "^3.0.2" + "semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -10142,6 +10718,11 @@ stream-splicer@^2.0.0: inherits "^2.0.1" readable-stream "^2.0.2" +string-convert@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" + integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -10316,6 +10897,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +stylis@^4.0.13: + version "4.3.1" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.1.tgz#ed8a9ebf9f76fe1e12d462f5cc3c4c980b23a7eb" + integrity sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ== + subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" @@ -10453,6 +11039,11 @@ throat@^4.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" integrity sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA== +throttle-debounce@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933" + integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg== + through2@2.0.x, through2@2.X, through2@^2.0.0, through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -10564,6 +11155,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" From 9521d0b6d97571a5d5665fc72f1e832b2f6f300b Mon Sep 17 00:00:00 2001 From: mlluciano Date: Mon, 15 Apr 2024 12:14:35 -0400 Subject: [PATCH 05/22] improve: add constant to enforce extended specimen columns order --- public/client/js/react/src/record.js | 33 +++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 629ad0c8..3a5ea830 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -13,6 +13,27 @@ const NO_DEMO_BGCOLOR = false, DEMO_BGCOLOR = 'palegreen'; +const extendedSpecimenOrder = { + "idhistory" : { + "dwc:scientificName": 1, + "dwc:identifiedBy": 3, + "idigbio:recordID": 6, + "coreid": 4, + "dwc:scientificNameAuthorship": 5, + "dwc:dateIdentified": 2, + "dcterms:modified": 7, + }, + "extendedmeasurementorfact": { + "dwc:measurementDeterminedDate": 1, + "dwc:measurementType": 2, + "coreid": 3, + "obis:measurementTypeID": 4, + "obis:measurementValueID": 5, + "dwc:measurementDeterminedBy": 6, + "dwc:measurementValue": 7 + } +} + const Row = ({keyid, data}) => { var name = _.isUndefined(dwc.names[keyid]) ? keyid : dwc.names[keyid]; @@ -151,12 +172,8 @@ const Record = ({record, raw }) => { }, []) } - function getAntdColumns(keys) { // takes a list of keys and formats them to be used as antd column headers - const sorted_keys = keys.sort((a, b) => { - if (a === 'dwc:scientificName') return -1; - if (b === 'dwc:scientificName') return 1; - return 0; - }) + function getAntdColumns(keys, sec) { // takes a list of keys and formats them to be used as antd column headers + const sorted_keys = keys.sort((a, b) => extendedSpecimenOrder[sec][a] - extendedSpecimenOrder[sec][b]) return sorted_keys.map(key => ({ title: _.isUndefined(dwc.names[key]) ? key : dwc.names[key], dataIndex: key, @@ -177,7 +194,7 @@ const Record = ({record, raw }) => { function getAntdTable(recordSection, sec) { const allKeys = extractKeys(recordSection) - const columns = getAntdColumns(allKeys) + const columns = getAntdColumns(allKeys, sec) const rows = completeData(recordSection, allKeys) return (
    {dwc.names[sec]}
    @@ -207,7 +224,7 @@ const Record = ({record, raw }) => { if (cnt === 0) { active = true; } - if (sec === 'idhistory' || sec === 'extendedmeasurementorfact') { + if (sec in extendedSpecimenOrder) { //FIXME Implementation duplicated below (at "Render Identification History into its own tab") if(!Array.isArray(record[sec])){ console.error('error creating section \'idhistory\': not an array'); From 875d87bc04cc495865cf6c151d01efec653cf769 Mon Sep 17 00:00:00 2001 From: valdeza Date: Wed, 17 Apr 2024 19:15:40 +0000 Subject: [PATCH 06/22] draft: improve: column hiding for specimen record data extension tables NOTE: Contains draft code. See current draft code notes at /DRAFT-UNRESOLVED.txt 1. And apply that column hiding to 'coreid'. They refer to the record the user is already looking at. Besides, the coreids some providers use are /very long/. 2. Consolidate table code to use var `extendedSpecimenOrder`. The fewer things to update for future extensions, the better. Also: Added FIXME note on column sorting. Doesn't work in some cases. Shouldn't be a dealbreaker, I think. --- public/client/js/react/src/record.js | 49 +++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 3a5ea830..2de5a79b 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -13,12 +13,25 @@ const NO_DEMO_BGCOLOR = false, DEMO_BGCOLOR = 'palegreen'; +const ESO_HIDE_FIELD = -1; +// Sections defined here will be given a table at the end of +// the record page, data section. +// For all fields defined within each section, +// set an integer to denote column ordering, +// where lower integers appear first/left-most. +// Undefined fields will be appended to the last/right-most end of the table. +// Fields set to ESO_HIDE_FIELD will not be displayed in the table. +// +//FIXME// Column sorting might not work properly. +// Example: For specimen where 'occurrence ID' is http://n2t.net/ark:/65665/319944bac-6847-4105-bb85-0cd03f5efad3, +// got column order: [..., Ratio of Absorbance (260/230 nm), Sample Designation, Ratio of Absorbance (260/280 nm)] +// expected 'Sample Designation' to be last const extendedSpecimenOrder = { "idhistory" : { "dwc:scientificName": 1, "dwc:identifiedBy": 3, "idigbio:recordID": 6, - "coreid": 4, + "coreid": ESO_HIDE_FIELD, "dwc:scientificNameAuthorship": 5, "dwc:dateIdentified": 2, "dcterms:modified": 7, @@ -26,7 +39,7 @@ const extendedSpecimenOrder = { "extendedmeasurementorfact": { "dwc:measurementDeterminedDate": 1, "dwc:measurementType": 2, - "coreid": 3, + "coreid": ESO_HIDE_FIELD, "obis:measurementTypeID": 4, "obis:measurementValueID": 5, "dwc:measurementDeterminedBy": 6, @@ -161,10 +174,18 @@ const Record = ({record, raw }) => { setActive(e.target.attributes['data-tab'].value) } - function extractKeys(arr) { // extracts keys from array of objects + /** Extracts keys from array of objects. + * + * {@link sec} is used for filtering out columns designated hidden + * (see {@link extendedSpecimenOrder}). + * + * @param {object[]} arr Section data array + * @param {string} sec Section name + */ + function extractKeys(arr, sec) { return arr.reduce((keys, obj) => { Object.keys(obj).forEach(key => { - if (!keys.includes(key)) { + if (!keys.includes(key) && extendedSpecimenOrder[sec][key] != ESO_HIDE_FIELD) { keys.push(key); } }); @@ -192,8 +213,13 @@ const Record = ({record, raw }) => { }); } + /** + * @param {object[]} recordSection Section data array + * @param {string} sec Section name + * @returns Section HTML for the given parameters, including header title + */ function getAntdTable(recordSection, sec) { - const allKeys = extractKeys(recordSection) + const allKeys = extractKeys(recordSection, sec) const columns = getAntdColumns(allKeys, sec) const rows = completeData(recordSection, allKeys) return (
    @@ -213,10 +239,13 @@ const Record = ({record, raw }) => { useEffect(() => { var has = []; + /** @type {React.JSX.Element[]} */ var non_props_record = []; + /** @type {React.JSX.Element[]} */ var record_id_history = []; - var sorder = ['extendedspecimen', 'taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', 'idhistory', 'extendedmeasurementorfact', 'other']; + var sorder = ['extendedspecimen', 'taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', ...Object.keys(extendedSpecimenOrder), 'other']; var cnt = 0; + // Record tab rendering sorder.forEach(function (sec, index) { if (_.has(record, sec)) { @@ -454,7 +483,7 @@ const RecordPage = ({ record }) => { _.defaults(canonical, data); _.each(dwc.order, function (val, key) { - if (key === 'idhistory' || key === 'extendedmeasurementorfact') { + if (key in extendedSpecimenOrder) { /* Requires special handling to flatten: * Unlike other sections, this one is an array. * @@ -490,7 +519,11 @@ const RecordPage = ({ record }) => { * }] * } */ - const fld = key === 'idhistory' ? 'dwc:Identification' : 'obis:ExtendedMeasurementOrFact' ; + const fld = dwc.order[key][0]; + if (dwc.order[key].length > 1) { + // If this soft assert fails, key might correspond to the incorrect DwC field + console.warn("More than one value for dwc_fields order key '%s'. Using first value '%s'.", key, fld); + } if (_.has(canonical, fld)) { if (!_.has(localRecord, key)) { localRecord[key] = []; From e3a1b869e99ab0ee72c79f67001229a16eaeee4b Mon Sep 17 00:00:00 2001 From: valdeza Date: Wed, 17 Apr 2024 20:15:10 +0000 Subject: [PATCH 07/22] draft: feat: add record extension tables for ggbn:MaterialSample & chrono:ChronometricAge NOTE: Draft code present. See current draft code notes at /DRAFT-UNRESOLVED.txt Also added labels to obis:ExtendedMeasurementOrFact table --- public/client/js/lib/dwc_fields.js | 36 +++++++++++++++++++++++++--- public/client/js/react/src/record.js | 30 ++++++++++++++++++----- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/public/client/js/lib/dwc_fields.js b/public/client/js/lib/dwc_fields.js index 05535f7c..78b58647 100644 --- a/public/client/js/lib/dwc_fields.js +++ b/public/client/js/lib/dwc_fields.js @@ -133,6 +133,12 @@ module.exports = { "extendedmeasurementorfact": [ "obis:ExtendedMeasurementOrFact" ], + "materialsample": [ + "ggbn:MaterialSample" + ], + "chronometricage": [ + "chrono:ChronometricAge" + ], "other": [ "dcterms:language", "dcterms:modified", @@ -489,11 +495,35 @@ module.exports = { "idigbio:importantHoldings": "Important Holdings", "extendedspecimen": "Links DEMOLOCATION 01", "dwc:associatedOccurrences": "Associated Occurrences", + "idhistory": "Identification History", - "extendedmeasurementorfact": "Extended Measurement or Fact", "dwc:identificationID": "Identification ID", "dwc:verbatimIdentification": "Verbatim Identification", "dwc:identifiedByID": "Identified By (ID)", - "dwc:identificationVerificationStatus": "Identification Verification Status" + "dwc:identificationVerificationStatus": "Identification Verification Status", + + "extendedmeasurementorfact": "Extended Measurement or Fact", + "dwc:measurementDeterminedBy": "Measurement Determined By", + "dwc:measurementDeterminedDate": "Measurement Determined Date", + "dwc:measurementType": "Measurement Type", + "dwc:measurementValue": "Measurement Value", + "obis:measurementTypeID": "Measurement Type ID", + "obis:measurementValueID": "Measurement Value ID", + + "materialsample": "Material Sample", + "ggbn:concentrationUnit": "Concentration Unit", + "ggbn:concentration": "Concentration", + "ggbn:materialSampleType": "Material Sample Type", + "ggbn:ratioOfAbsorbance260_230": "Ratio of Absorbance (260/230 nm)", //| For DNA samples only + "ggbn:ratioOfAbsorbance260_280": "Ratio of Absorbance (260/280 nm)", //| + "ggbn:sampleDesignation": "Sample Designation", + + "chronometricage": "Chronometric Age", + "chrono:chronometricAgeReferences": "Chronometric Age References", + "chrono:chronometricAgeRemarks": "Chronometric Age Remarks", + "chrono:maximumChronometricAge": "Maximum Chronometric Age", + "chrono:maximumChronometricAgeReferenceSystem": "Maximum Chronometric Age Reference System", + "chrono:minimumChronometricAge": "Minimum Chronometric Age", + "chrono:minimumChronometricAgeReferenceSystem": "Minimum Chronometric Age Reference System", } -} \ No newline at end of file +} diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 2de5a79b..585278ea 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -37,14 +37,32 @@ const extendedSpecimenOrder = { "dcterms:modified": 7, }, "extendedmeasurementorfact": { - "dwc:measurementDeterminedDate": 1, - "dwc:measurementType": 2, "coreid": ESO_HIDE_FIELD, - "obis:measurementTypeID": 4, + "dwc:measurementValue": 1, + "dwc:measurementType": 2, + "dwc:measurementDeterminedBy": 3, + "dwc:measurementDeterminedDate": 4, "obis:measurementValueID": 5, - "dwc:measurementDeterminedBy": 6, - "dwc:measurementValue": 7 - } + "obis:measurementTypeID": 6, + }, + "materialsample": { + "coreid": ESO_HIDE_FIELD, + "ggbn:materialSampleType": 1, + "ggbn:concentrationUnit": 2, + "ggbn:concentration": 3, + "ggbn:ratioOfAbsorbance260_230": 4, + "ggbn:ratioOfAbsorance260_280": 5, + "ggbn:sampleDesignation": 6, + }, + "chronometricage": { + "coreid": ESO_HIDE_FIELD, + "chrono:minimumChronometricAge": 1, + "chrono:minimumChronometricAgeReferenceSystem": 2, + "chrono:maximumChronometricAge": 3, + "chrono:maximumChronometricAgeReferenceSystem": 4, + "chrono:chronometricAgeRemarks": 5, + "chrono:chronometricAgeReferences": 6, + }, } const Row = ({keyid, data}) => { From b9b9edcbfecc2ceb4ace5b07e20ee69950a9c350 Mon Sep 17 00:00:00 2001 From: valdeza Date: Thu, 18 Apr 2024 14:42:00 +0000 Subject: [PATCH 08/22] chore: remove draft code Per previous discussions, we will be keeping all data in a single tab. Multi-value extensions will be displayed at the bottom of the page. --- DRAFT-UNRESOLVED.txt | 22 --------- public/client/js/lib/dwc_fields.js | 4 -- public/client/js/react/src/record.js | 69 +++++++--------------------- public/client/less/record.less | 2 +- public/css/record.css | 10 ++-- 5 files changed, 21 insertions(+), 86 deletions(-) delete mode 100644 DRAFT-UNRESOLVED.txt diff --git a/DRAFT-UNRESOLVED.txt b/DRAFT-UNRESOLVED.txt deleted file mode 100644 index 2bade62e..00000000 --- a/DRAFT-UNRESOLVED.txt +++ /dev/null @@ -1,22 +0,0 @@ -(Remove this file when resolved) - -Make the following corrections before use in production: -/public/client/less/record.less:: -- If not putting identification history in separate tab in record page, - remove #idhistory and run gulp task 'buildLess' to regenerate CSS stylesheets - (depends: /public/css/record.css) -/public/client/js/lib/dwc_fields.js:: -- "dwc:associatedOccurrences" is duplicated and will appear twice in - specimen record pages. -- Value for $.names.extendedspecimen has an inappropriate value. -/public/client/js/react/src/record.js:: -- New fields are given CSS property 'backgroundColor: palegreen' - for visibility. Find and remove all usages of 'DEMO_BGCOLOR'. -- If not giving 'extendedspecimen' its own section, remove from array - assigned to `var sorder` -- "dwc:Identification" is duplicated and will appear twice in - specimen record pages: - 1. Dedicated ID History tab - 2. Added sections near bottom of Data tab -/public/css/record.css:: - This file is automatically generated. See /public/client/less/record.less diff --git a/public/client/js/lib/dwc_fields.js b/public/client/js/lib/dwc_fields.js index 78b58647..b0a0a2ab 100644 --- a/public/client/js/lib/dwc_fields.js +++ b/public/client/js/lib/dwc_fields.js @@ -124,9 +124,6 @@ module.exports = { "dwc:lowestBiostratigraphicZone", "dwc:lithostratigraphicTerms" ], - "extendedspecimen": [ - "dwc:associatedOccurrences" - ], "idhistory": [ "dwc:Identification" ], @@ -493,7 +490,6 @@ module.exports = { "idigbio:collectionCategory": "Collection Category", "idigbio:collectionSize": "Collection Size", "idigbio:importantHoldings": "Important Holdings", - "extendedspecimen": "Links DEMOLOCATION 01", "dwc:associatedOccurrences": "Associated Occurrences", "idhistory": "Identification History", diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 585278ea..56209bbb 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -9,10 +9,6 @@ import dqFlags from '../../lib/dq_flags'; import idbapi from '../../lib/idbapi'; import { Table } from 'antd'; -const - NO_DEMO_BGCOLOR = false, - DEMO_BGCOLOR = 'palegreen'; - const ESO_HIDE_FIELD = -1; // Sections defined here will be given a table at the end of // the record page, data section. @@ -75,7 +71,7 @@ const Row = ({keyid, data}) => { }); return ( -
    + @@ -86,9 +82,7 @@ const Row = ({keyid, data}) => { /** * @param {object} props * @param {string} props.name Section ID name, corresponding to dwc_fields.js. - * If 'idhistory:', {@link data} is handled differently. - * @param {object|object[]} props.data Field key-values specific to this section. - * If {@link name} is 'idhistory:', this will instead be expected to be an array. + * @param {object} props.data Field key-values specific to this section. * @param {boolean} props.active If `true`, assign CSS class to allow this section to be visible. */ const Section = ({name, data, active}) => { @@ -96,33 +90,23 @@ const Section = ({name, data, active}) => { var rows = [] var data = data; - if(name.startsWith('idhistory')){ - _.forIn(data,function(fieldValue, fieldKey){ - if(_.isString(fieldValue)){ - rows.push(); - } - }) - }else{ - _.each(data,function(fld){ - var key = Object.keys(fld)[0]; - if(_.isString(fld[key])){ - rows.push(); - } - }); - } + _.each(data,function(fld){ + var key = Object.keys(fld)[0]; + console.debug('fld=', fld, ', key=', key); + if(_.isString(fld[key])){ + rows.push(); + } + }); var cl = "section visible-print-block"; if(active){ cl="section"; } - let isDemoContent = !NO_DEMO_BGCOLOR && (['extendedspecimen'].includes(name) || name.startsWith('idhistory')); /** @type {string} */ - let sectionName = (name.startsWith('idhistory') && name.includes(':') - ? dwc.names['idhistory'] +' '+ name.split(':')[1] - : dwc.names[name]); + let sectionName = dwc.names[name]; return ( -
    +
    {sectionName}
    -
    {name}
    +
    {rows}
    @@ -161,7 +145,6 @@ const Flags = ({flags, active}) => { const Record = ({record, raw }) => { const [active, setActive] = useState("record") const [nonPropsRecord, setNonPropsRecord] = useState([]) - const [recordIdHistory, setRecordIdHistory] = useState([]) function formatJSON(json){ if (typeof json != 'string') { @@ -255,13 +238,15 @@ const Record = ({record, raw }) => { } useEffect(() => { + console.log('record=', record); + console.log('raw=', raw); var has = []; /** @type {React.JSX.Element[]} */ var non_props_record = []; /** @type {React.JSX.Element[]} */ var record_id_history = []; - var sorder = ['extendedspecimen', 'taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', ...Object.keys(extendedSpecimenOrder), 'other']; + var sorder = ['taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', ...Object.keys(extendedSpecimenOrder), 'other']; var cnt = 0; // Record tab rendering @@ -272,9 +257,8 @@ const Record = ({record, raw }) => { active = true; } if (sec in extendedSpecimenOrder) { - //FIXME Implementation duplicated below (at "Render Identification History into its own tab") if(!Array.isArray(record[sec])){ - console.error('error creating section \'idhistory\': not an array'); + console.error('error creating section \'%s\': not an array', sec); }else{ non_props_record.push(getAntdTable(record[sec], sec)) } @@ -286,40 +270,21 @@ const Record = ({record, raw }) => { } }); setNonPropsRecord([...nonPropsRecord, non_props_record]) - - // Render Identification History into its own tab, if applicable. - // - // Basically duplicate code from the 'idhistory' carve-out for Record tab rendering above - // (while indecisive about where we want to put this) - let sec = 'idhistory'; - if(_.has(record,sec)){ - var active=true; - if(!Array.isArray(record[sec])){ - console.error('error creating section \'idhistory\': not an array'); - }else{ - record_id_history = getAntdTable(record[sec], sec) - } - } - setRecordIdHistory([...recordIdHistory, record_id_history]) }, []); - let - doRenderIdHistory = !!raw.indexTerms?.indexData?.['dwc:Identification'], - doRenderFlags = !!raw.indexTerms.flags; + let doRenderFlags = !!raw.indexTerms.flags; return (
    • Data
    • - {doRenderIdHistory ?
    • ID History
    • : ''} {doRenderFlags ?
    • Flags
    • : ''}
    • Raw
    {nonPropsRecord}
    - {doRenderIdHistory ?
    {recordIdHistory}
    : ''} {doRenderFlags ? : ''}

    diff --git a/public/client/less/record.less b/public/client/less/record.less index 5549e815..eb1af3b2 100644 --- a/public/client/less/record.less +++ b/public/client/less/record.less @@ -134,7 +134,7 @@ h1#banner{ #data{ margin-top:30px; - #record, #flags, #idhistory{ + #record, #flags{ border-top:@brd; font-size:13px; padding-top:20px; diff --git a/public/css/record.css b/public/css/record.css index cfa2ed48..4040a367 100644 --- a/public/css/record.css +++ b/public/css/record.css @@ -252,24 +252,20 @@ h1#banner { margin-top: 30px; } #data #record, -#data #flags, -#data #idhistory { +#data #flags { border-top: 1px solid #ccc; font-size: 13px; padding-top: 20px; } #data #record .field-name, -#data #flags .field-name, -#data #idhistory .field-name { +#data #flags .field-name { font-weight: normal; color: #555; } #data #record .field-value, #data #flags .field-value, -#data #idhistory .field-value, #data #record .field-value a, -#data #flags .field-value a, -#data #idhistory .field-value a { +#data #flags .field-value a { word-wrap: break-all; word-break: break-word; overflow: hidden; From 202e56da73e4842158fd333279d6eee73078f84d Mon Sep 17 00:00:00 2001 From: valdeza Date: Wed, 24 Apr 2024 18:59:34 +0000 Subject: [PATCH 09/22] improve: record extension table styling now matches surrounding tables 'antd', the library we're using for these tables, likes to push its own styling, much to the annoyance to many other antd-5 users I came across when I was looking up how to override them. --- public/client/js/react/src/record.js | 30 +++++++----- public/client/less/record.less | 73 ++++++++++++++++++++-------- public/css/record.css | 39 +++++++++++++++ 3 files changed, 110 insertions(+), 32 deletions(-) diff --git a/public/client/js/react/src/record.js b/public/client/js/react/src/record.js index 56209bbb..9babcb54 100644 --- a/public/client/js/react/src/record.js +++ b/public/client/js/react/src/record.js @@ -7,7 +7,7 @@ import moment from 'moment'; import fields from '../../lib/fields'; import dqFlags from '../../lib/dq_flags'; import idbapi from '../../lib/idbapi'; -import { Table } from 'antd'; +import { ConfigProvider, Table } from 'antd'; const ESO_HIDE_FIELD = -1; // Sections defined here will be given a table at the end of @@ -223,17 +223,23 @@ const Record = ({record, raw }) => { const allKeys = extractKeys(recordSection, sec) const columns = getAntdColumns(allKeys, sec) const rows = completeData(recordSection, allKeys) - return (

    + return (
    {dwc.names[sec]}
    - index % 2 === 0 ? 'evenRow' : 'oddRow'} - columns={columns} - dataSource={rows} - scroll={{x: 'max-content'}} - pagination={false} - size={"small"} - /> + +
    index % 2 === 0 ? 'evenRow' : 'oddRow'} + columns={columns} + dataSource={rows} + scroll={{x: 'max-content'}} + pagination={false} + /> + ) } @@ -244,8 +250,6 @@ const Record = ({record, raw }) => { var has = []; /** @type {React.JSX.Element[]} */ var non_props_record = []; - /** @type {React.JSX.Element[]} */ - var record_id_history = []; var sorder = ['taxonomy', 'specimen', 'collectionevent', 'locality', 'paleocontext', ...Object.keys(extendedSpecimenOrder), 'other']; var cnt = 0; diff --git a/public/client/less/record.less b/public/client/less/record.less index eb1af3b2..3859d64a 100644 --- a/public/client/less/record.less +++ b/public/client/less/record.less @@ -86,7 +86,7 @@ h1#banner{ } } .sec{ - font-size: 13px; + font-size: @data-font-size; table{ td{ padding: 1px 0px; @@ -105,38 +105,73 @@ h1#banner{ } } } -//.customTable .ant-table { -// background-color: #f0f2f5; /* Light grey background */ -//} -.custom-antd-table .ant-table-tbody > tr.oddRow > td { - background-color: #f9f9f9; /* Light grey background for odd rows */ - font-size: 13px; -} -.custom-antd-table .ant-table-tbody > tr.evenRow > td { - background-color: #ffffff; /* White background for even rows */ - font-size: 13px; +// Overrides for antd default table styling +.custom-antd-border() { + border: 1px solid #ddd; /* Solid gray border */ } +.custom-antd-cell-properties() { + //corresponds to: #bootstrap .table-condensed tr {th,td} + padding: 5px; -.custom-antd-table .ant-table-thead > tr > th { - font-size: 13px; - border-radius: 0; + //corresponds to: #bootstrap .table-bordered tr {th,td} + .custom-antd-border(); } +.custom-antd-table { + &.ant-table-wrapper table { + //from: #bootstrap.table() + margin-bottom: 20px; -.custom-antd-table .ant-table { - border: 1px solid #ddd; /* Solid gray border */ -} + //corresponds to: #bootstrap.table-bordered(); + .custom-antd-border(); + + //corresponds to: #bootstrap.table(); + border-collapse: collapse; + } + + [class^='ant-table'], [class*='ant-table'] { + font-size: @data-font-size; + + //from: #bootstrap.body(); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + } + + .ant-table-thead > tr > th { + .custom-antd-cell-properties(); + } + .ant-table-tbody > tr > td { + .custom-antd-cell-properties(); + } + + .ant-table { + color: @data-text-color; + } + + .ant-table-tbody > tr.oddRow > td { + //corresponds to: #bootstrap > .table-striped > tbody > tr:nth-of-type(odd): + background-color: #f9f9f9; /* Light grey background for odd rows */ + } + .ant-table-tbody > tr.evenRow > td { + background-color: #ffffff; /* White background for even rows */ + } + .ant-table-thead > tr > th { + border-radius: 0; + border-bottom-width: 2px; + } +} .ant-table { border-radius: 0 !important; } +@data-text-color: #555; +@data-font-size: 13px; #data{ margin-top:30px; #record, #flags{ border-top:@brd; - font-size:13px; + font-size:@data-font-size; padding-top:20px; .field-name{ font-weight:normal; @@ -149,7 +184,7 @@ h1#banner{ word-break:break-word; overflow:hidden; white-space: pre-line; - color:#555; + color:@data-text-color; } } @import 'tabs.less'; diff --git a/public/css/record.css b/public/css/record.css index 4040a367..c8b134bf 100644 --- a/public/css/record.css +++ b/public/css/record.css @@ -248,6 +248,45 @@ h1#banner { text-align: left; padding-left: 8px; } +.custom-antd-table.ant-table-wrapper table { + margin-bottom: 20px; + border: 1px solid #ddd; + /* Solid gray border */ + border-collapse: collapse; +} +.custom-antd-table [class^='ant-table'], +.custom-antd-table [class*='ant-table'] { + font-size: 13px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.custom-antd-table .ant-table-thead > tr > th { + padding: 5px; + border: 1px solid #ddd; + /* Solid gray border */ +} +.custom-antd-table .ant-table-tbody > tr > td { + padding: 5px; + border: 1px solid #ddd; + /* Solid gray border */ +} +.custom-antd-table .ant-table { + color: #555; +} +.custom-antd-table .ant-table-tbody > tr.oddRow > td { + background-color: #f9f9f9; + /* Light grey background for odd rows */ +} +.custom-antd-table .ant-table-tbody > tr.evenRow > td { + background-color: #ffffff; + /* White background for even rows */ +} +.custom-antd-table .ant-table-thead > tr > th { + border-radius: 0; + border-bottom-width: 2px; +} +.ant-table { + border-radius: 0 !important; +} #data { margin-top: 30px; } From aa5bc3853a2daaf24f73b07dc50bccb70bf494ae Mon Sep 17 00:00:00 2001 From: valdeza Date: Thu, 25 Apr 2024 13:51:03 +0000 Subject: [PATCH 10/22] chore: bump copyright year --- app/views/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/base.html b/app/views/base.html index b44a4acf..e7d65c99 100644 --- a/app/views/base.html +++ b/app/views/base.html @@ -140,7 +140,7 @@
    -

    iDigBio is funded by grants from the National Science Foundation [DBI-1115210 (2011-2018), DBI-1547229 (2016-2022), & DBI-2027654 (2021-2026)]. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. © 2011-2023 iDigBio

    +

    iDigBio is funded by grants from the National Science Foundation [DBI-1115210 (2011-2018), DBI-1547229 (2016-2022), & DBI-2027654 (2021-2026)]. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation. © 2011-2024 iDigBio

    From b36aa706f6622665bc15aa7001e81b6d842a8a43 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Wed, 8 May 2024 13:27:10 -0400 Subject: [PATCH 11/22] fix: corrected issue where links to the portal would not parse query parameters from URL. --- public/client/js/react/src/search.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/public/client/js/react/src/search.js b/public/client/js/react/src/search.js index 19abcfa3..f8ab4763 100644 --- a/public/client/js/react/src/search.js +++ b/public/client/js/react/src/search.js @@ -15,6 +15,7 @@ const Search = () => { const [optionsTab, setOptionsTab] = useState('filters') const [resultsTab, setResultsTab] = useState('list') const [search, setSearch] = useState(defaultSearch()) + const [ready, setReady] = useState(false) function defaultSearch(){ return { filters: defaultFilters(), @@ -70,6 +71,7 @@ const Search = () => { searchHistory.push(currentSearch); // Update the state with 'search' setSearch(currentSearch) + setReady(true) }, []); function searchChange(key,val){ @@ -97,16 +99,21 @@ const Search = () => { } } - return( + return (
    -
    - - -
    - + {ready && ( + <> +
    + + +
    + + + )}
    ) From 4c80322ff5d5060ec96f4e433be16ebc38e0324f Mon Sep 17 00:00:00 2001 From: jbennettufl Date: Wed, 8 May 2024 14:32:59 -0400 Subject: [PATCH 12/22] update relative date changes again --- app/controllers/publishers.js | 2 +- public/client/js/react/src/shared/stats_charts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/publishers.js b/app/controllers/publishers.js index d53674d6..50e85696 100644 --- a/app/controllers/publishers.js +++ b/app/controllers/publishers.js @@ -4,7 +4,7 @@ import async from 'async' import _ from 'lodash' import RecordsetPage from 'public/client/js/react/build/recordset' import StatsPage from 'public/client/js/react/build/stats' - +import moment from 'moment' import React from 'react' import ReactDOMServer from 'react-dom/server' diff --git a/public/client/js/react/src/shared/stats_charts.js b/public/client/js/react/src/shared/stats_charts.js index be44d67f..3ca29a83 100644 --- a/public/client/js/react/src/shared/stats_charts.js +++ b/public/client/js/react/src/shared/stats_charts.js @@ -296,7 +296,7 @@ const TaxonPies = ({data}) => { } const StatsCharts = (props) => { - const [startDate, setStartDate] = useState(moment("2015-01-16", "YYYY-MM-DD")); + const [startDate, setStartDate] = useState(moment().subtract(3,'years').startOf('month')); const [endDate, setEndDate] = useState(moment().subtract(1, 'months').endOf('month')); const [log, setLog] = useState(true); const [cumulative, setCumulative] = useState(true); From 536ecb0c8233a4047654a483855ade445ccf8082 Mon Sep 17 00:00:00 2001 From: jbennettufl Date: Wed, 8 May 2024 15:02:29 -0400 Subject: [PATCH 13/22] update publisher date to use moment offset --- app/controllers/publishers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/publishers.js b/app/controllers/publishers.js index 50e85696..5526472c 100644 --- a/app/controllers/publishers.js +++ b/app/controllers/publishers.js @@ -122,7 +122,7 @@ export default { var collected = {}; var taxon = {}; var flags = {}; - var defaultMin = "2015-01-16"; + var defaultMin = moment().subtract(3,'years').startOf('month'); async.parallel([ function(cback) { var params = {"dateInterval": "month", "minDate": defaultMin}; @@ -271,7 +271,7 @@ export default { }); }, function(cback) { - var params = {"dateInterval": "month", "recordset": req.params.id, "minDate": "2015-01-15"}; + var params = {"dateInterval": "month", "recordset": req.params.id, "minDate": moment().subtract(3,'years').startOf('month')}; request.post({"url": config.api + 'summary/stats/search', "json": true, "body": params}, function(a_err, a_resp, a_body) { use = a_body; cback(a_err, 'four'); From e3831dd3c763aafd5090857cbfd797d2d757d669 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Tue, 14 May 2024 12:19:01 -0400 Subject: [PATCH 14/22] fix: media tab in results now displays media from parsed url params --- public/client/js/react/src/search/results.js | 100 ++++++++----------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/public/client/js/react/src/search/results.js b/public/client/js/react/src/search/results.js index 79e7a223..087d2f2d 100644 --- a/public/client/js/react/src/search/results.js +++ b/public/client/js/react/src/search/results.js @@ -3,60 +3,54 @@ import idbapi from '../../../lib/idbapi'; import queryBuilder from '../../../lib/querybuilder'; -const Results = ({ searchProp, searchChange, view, viewChange }) => { +const Results = ({ searchProp, searchChange, view, viewChange, aggs, setAggs }) => { const [lastQueryStringed, setLastQueryStringed] = useState(''); const [results, setResults] = useState([]); const [attribution, setAttribution] = useState([]); const [total, setTotal] = useState(0); - const [search, setSearch] = useState(searchProp); const [hasMore, setHasMore] = useState(false); const [loading, setLoading] = useState(true); const lastQueryTimeRef = useRef(); const getResults = useCallback(() => { let now = new Date().getTime(); - let query = queryBuilder.makeSearchQuery(search); + let query = queryBuilder.makeSearchQuery(searchProp); if (JSON.stringify(query) !== lastQueryStringed) { - if (search.from === 0) { + if (searchProp.from === 0) { setLoading(true); setResults([]); } lastQueryTimeRef.current = now; idbapi.search(query, function (response) { - let searchState = search + let searchState = searchProp if (response.error !== 'Internal Server Error') { if (now >= lastQueryTimeRef.current) { let res = search.from > 0 ? results.concat(response.items) : response.items; - let more = response.itemCount > (search.from + search.size); + let more = response.itemCount > (searchProp.from + searchProp.size); searchState.from = query.offset; - setSearch(searchState) + searchChange(searchState) setResults(res); setAttribution(response.attribution); setTotal(response.itemCount); setHasMore(more); setLoading(false); + setAggs(response.aggs.unique_scientific_names.buckets) } } }); setLastQueryStringed(JSON.stringify(query)); } - }, [search, results, lastQueryStringed]); - - useEffect(() => { - if (searchProp!==search) { - setSearch(_.cloneDeep(searchProp)); - } - }, [searchProp]); + }, [searchProp, results, lastQueryStringed]); useEffect(() => { getResults(); - }, [search]); + }, [searchProp]); useEffect(() => { const handleScroll = () => { - let newSearch = _.cloneDeep(search); + let newSearch = _.cloneDeep(searchProp); if (total > newSearch.from + newSearch.size) { if (($(window).scrollTop() + 40 >= $(document).height() - $(window).height()) && (!loading)) { newSearch.from += newSearch.size; @@ -78,7 +72,7 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { }; const updateResults = (newSearch) => { - setSearch(newSearch); + searchChange(newSearch) setLoading(true); getResults(); }; @@ -87,14 +81,14 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { let resultsComponent; switch (view) { case 'list': - resultsComponent = ; + resultsComponent = ; break; case 'labels': resultsComponent = ; break; case 'media': - resultsComponent = ; + resultsComponent = ; break; case 'recordsets': resultsComponent = ; @@ -134,7 +128,7 @@ const ResultsList = ({search, searchChange, results, loading}) => { }, []) function resetColumns(){ - setColumns(defaultColumns()); + setColumns(defaultColumns()); } function defaultColumns(){ return ['family','scientificname','datecollected','country','institutioncode','basisofrecord']; @@ -168,7 +162,7 @@ const ResultsList = ({search, searchChange, results, loading}) => { //sorted column sorts the top level sort value in search and new sorting items length //shall not exceed original length var dir, localSearch = _.cloneDeep(search), name=e.currentTarget.attributes['data-term'].value, - sort={name: name}, sorting=localSearch.sorting, curlength = sorting.length; + sort={name: name}, sorting=localSearch.sorting, curlength = sorting.length; if(_.isUndefined(e.currentTarget.attributes['data-sort'])){ dir='asc'; }else{ @@ -197,14 +191,14 @@ const ResultsList = ({search, searchChange, results, loading}) => { //to prevent opening if hiliting text if(window.getSelection().toString().length===0 || (e.target.nodeName=='I' || e.target.nodeName=='BUTTON')){ - window.open('/portal/records/'+e.currentTarget.id,e.currentTarget.id); + window.open('/portal/records/'+e.currentTarget.id,e.currentTarget.id); } } var cols = columns,self=this; - //['scientificname','genus','collectioncode','specificepithet','commonname']; + //['scientificname','genus','collectioncode','specificepithet','commonname']; var rows=[]; var headers=[]; //results table @@ -330,7 +324,7 @@ const ResultsList = ({search, searchChange, results, loading}) => {
    - {list} + {list}
    @@ -342,10 +336,10 @@ const ResultsList = ({search, searchChange, results, loading}) => {
    - {headers} + {headers} - {rows} + {rows}
    @@ -445,25 +439,25 @@ class ResultListColumnSelector extends React.Component{ } }); fgroups.push( - -   {fltrs} - + +   {fltrs} + ); }); var updisabled = ( ind === 0 ); var downdisabled = ( ind === self.state.columns.length-1 ); selects.push(
    -
    - - -
    - - +
    + + +
    + +
    ); }); @@ -536,7 +530,7 @@ const ResultsLabels = ({results, loading, stamp}) => { content.push({formatedDC}); } - var l=[]; + var l=[]; ['dwc:country','dwc:stateProvince','dwc:county','dwc:locality'].forEach(function(item){ if(_.has(raw,item)){ l.push(raw[item]) @@ -695,21 +689,9 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => { } } useEffect(() => { - // if(search.image) { - // setResults(search.results); - // setLoading(false); - // } else { - getImageOnlyResults(search); - // } + getImageOnlyResults(search); }, [search]); - // function UNSAFE_componentWillReceiveProps(nextProps){ - // if(nextProps.search.image){ - // setResults(nextProps.results) - // setLoading(false) - // }else{ - // getImageOnlyResults(nextProps.search); - // } - // } + function makeImageText(data){ } @@ -730,8 +712,8 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => { {count} {name.join(' + src={idbapi.media_host + "v2/media/"+uuid+"?size=thumbnail"} + onError={errorImage}/>
    {_.capitalize(name.join(' '))} @@ -791,10 +773,10 @@ const Providers = ({attribution}) => {
    - + - {list} + {list}
    RecordsetRecords in resultsDescription
    RecordsetRecords in resultsDescription
    From 55eec904cf6ceec1d9bf04ff2a22ad5f6641acf1 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Tue, 14 May 2024 15:18:51 -0400 Subject: [PATCH 15/22] update google analytics --- public/client/js/react/src/stats.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/public/client/js/react/src/stats.js b/public/client/js/react/src/stats.js index 3345c8ad..9611cd1d 100644 --- a/public/client/js/react/src/stats.js +++ b/public/client/js/react/src/stats.js @@ -25,9 +25,14 @@ const Stats = ({usage, ingest, ingestCumulative, collected, taxon, flags}) => {
    From 2f2e039982496dbe212a2ddc98992fbecc4ba34e Mon Sep 17 00:00:00 2001 From: mlluciano Date: Wed, 15 May 2024 12:48:43 -0400 Subject: [PATCH 16/22] refactor: optimize rendering --- public/client/js/react/src/search.js | 5 +---- public/client/js/react/src/search/filters.js | 5 +---- public/client/js/react/src/search/map.js | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/public/client/js/react/src/search.js b/public/client/js/react/src/search.js index f8ab4763..ea5a5cc7 100644 --- a/public/client/js/react/src/search.js +++ b/public/client/js/react/src/search.js @@ -15,7 +15,7 @@ const Search = () => { const [optionsTab, setOptionsTab] = useState('filters') const [resultsTab, setResultsTab] = useState('list') const [search, setSearch] = useState(defaultSearch()) - const [ready, setReady] = useState(false) + function defaultSearch(){ return { filters: defaultFilters(), @@ -71,7 +71,6 @@ const Search = () => { searchHistory.push(currentSearch); // Update the state with 'search' setSearch(currentSearch) - setReady(true) }, []); function searchChange(key,val){ @@ -101,7 +100,6 @@ const Search = () => { return (
    - {ready && ( <>
    - )}
    ) diff --git a/public/client/js/react/src/search/filters.js b/public/client/js/react/src/search/filters.js index 0f713608..469960b1 100644 --- a/public/client/js/react/src/search/filters.js +++ b/public/client/js/react/src/search/filters.js @@ -144,9 +144,6 @@ $.widget("custom.IDBAutocomplete", $.ui.autocomplete, { const TextFilter = ({filter, changeFilter, removeFilter, search}) => { const [text, setText] = useState(filter.text) - useEffect(() => { - debounce(filter) - }, []); useEffect(() => { setText(filter.text) @@ -154,7 +151,7 @@ const TextFilter = ({filter, changeFilter, removeFilter, search}) => { const debouncedTextType = _.debounce(function(param){ changeFilter(param); - },700,{leading: false, trailing: true}); + },200,{leading: false, trailing: true}); function debounce(param) { debouncedTextType(param) diff --git a/public/client/js/react/src/search/map.js b/public/client/js/react/src/search/map.js index 6693fcc3..4529fe81 100644 --- a/public/client/js/react/src/search/map.js +++ b/public/client/js/react/src/search/map.js @@ -5,8 +5,9 @@ import * as helpers from '../../../lib/helpers'; let map; // Declare map variable const Map = (props) => { - const [currentQuery, setCurrentQuery] = useState(''); + const [currentQuery, setCurrentQuery] = useState(JSON.stringify(queryBuilder.buildQueryShim(props.search))); const mapRef = useRef(null); // To store the map instance without triggering re-renders + const searchRef = useRef(props.search); // Equivalent to componentDidMount and componentDidUpdate useEffect(() => { @@ -43,18 +44,28 @@ const Map = (props) => { }); } + if (!searchRef.current) { + searchRef.current = props.search + } + const query = queryBuilder.buildQueryShim(props.search); mapRef.current.query(query); - }, [props.search]); // Only re-run if props.search changes + }, []); // Equivalent to UNSAFE_componentWillReceiveProps and shouldComponentUpdate useEffect(() => { + const prevSearch = searchRef.current + if (prevSearch === props.search) { + return + } + const q = queryBuilder.buildQueryShim(props.search); const next = JSON.stringify(q); if (next !== currentQuery) { setCurrentQuery(next); mapRef.current.query(q); + searchRef.current = props.search } }, [props.search, currentQuery]); From 7aada596585740ced84a88029bbd092d01c61055 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Wed, 15 May 2024 13:03:26 -0400 Subject: [PATCH 17/22] reenable minification --- gulpfile.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 8fdf0ffa..3609bdbd 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -87,7 +87,7 @@ gulp.task('build', function() { .pipe(buffer()) // load and init sourcemaps .pipe(sourcemaps.init({loadMaps: true})) - // .pipe(uglify()) + .pipe(uglify()) // write sourcemaps .pipe(sourcemaps.write('./')) .pipe(gulp.dest('./public/client/js/react/build')); @@ -106,7 +106,7 @@ gulp.task('libs', function() { .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) // Add transformation tasks to the pipeline here. - // .pipe(uglify()) + .pipe(uglify()) .on('error', log.error) .pipe(sourcemaps.write('./')) .pipe(gulp.dest('./public/js/')); @@ -124,7 +124,7 @@ gulp.task('mapper', function() { .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) // Add transformation tasks to the pipeline here. - // .pipe(uglify()) + .pipe(uglify()) .on('error', log.error) .pipe(sourcemaps.write('./')) .pipe(gulp.dest('./public/js/')); @@ -142,7 +142,7 @@ gulp.task('client', function() { .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) // Add transformation tasks to the pipeline here. - // .pipe(uglify()) + .pipe(uglify()) .on('error', log.error) .pipe(sourcemaps.write('./')) .pipe(gulp.dest('./public/js/')); From 4df7d35d4939610f8820f344e22f6e31e3a83c73 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Thu, 16 May 2024 14:52:04 -0400 Subject: [PATCH 18/22] fix issues with results updating --- public/client/js/react/src/search/results.js | 46 ++++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/public/client/js/react/src/search/results.js b/public/client/js/react/src/search/results.js index 087d2f2d..add8d845 100644 --- a/public/client/js/react/src/search/results.js +++ b/public/client/js/react/src/search/results.js @@ -3,54 +3,60 @@ import idbapi from '../../../lib/idbapi'; import queryBuilder from '../../../lib/querybuilder'; -const Results = ({ searchProp, searchChange, view, viewChange, aggs, setAggs }) => { +const Results = ({ searchProp, searchChange, view, viewChange }) => { const [lastQueryStringed, setLastQueryStringed] = useState(''); const [results, setResults] = useState([]); const [attribution, setAttribution] = useState([]); const [total, setTotal] = useState(0); + const [search, setSearch] = useState(searchProp); const [hasMore, setHasMore] = useState(false); const [loading, setLoading] = useState(true); const lastQueryTimeRef = useRef(); const getResults = useCallback(() => { let now = new Date().getTime(); - let query = queryBuilder.makeSearchQuery(searchProp); + let query = queryBuilder.makeSearchQuery(search); if (JSON.stringify(query) !== lastQueryStringed) { - if (searchProp.from === 0) { + if (search.from === 0) { setLoading(true); setResults([]); } lastQueryTimeRef.current = now; idbapi.search(query, function (response) { - let searchState = searchProp + let searchState = search if (response.error !== 'Internal Server Error') { if (now >= lastQueryTimeRef.current) { let res = search.from > 0 ? results.concat(response.items) : response.items; - let more = response.itemCount > (searchProp.from + searchProp.size); + let more = response.itemCount > (search.from + search.size); searchState.from = query.offset; - searchChange(searchState) + setSearch(searchState) setResults(res); setAttribution(response.attribution); setTotal(response.itemCount); setHasMore(more); setLoading(false); - setAggs(response.aggs.unique_scientific_names.buckets) } } }); setLastQueryStringed(JSON.stringify(query)); } - }, [searchProp, results, lastQueryStringed]); + }, [search, results, lastQueryStringed]); useEffect(() => { - getResults(); + if (searchProp!==search) { + setSearch(_.cloneDeep(searchProp)); + } }, [searchProp]); + useEffect(() => { + getResults(); + }, [search]); + useEffect(() => { const handleScroll = () => { - let newSearch = _.cloneDeep(searchProp); + let newSearch = _.cloneDeep(search); if (total > newSearch.from + newSearch.size) { if (($(window).scrollTop() + 40 >= $(document).height() - $(window).height()) && (!loading)) { newSearch.from += newSearch.size; @@ -72,7 +78,7 @@ const Results = ({ searchProp, searchChange, view, viewChange, aggs, setAggs }) }; const updateResults = (newSearch) => { - searchChange(newSearch) + setSearch(newSearch); setLoading(true); getResults(); }; @@ -81,13 +87,13 @@ const Results = ({ searchProp, searchChange, view, viewChange, aggs, setAggs }) let resultsComponent; switch (view) { case 'list': - resultsComponent = ; + resultsComponent = ; break; case 'labels': resultsComponent = ; break; case 'media': - resultsComponent = ; break; case 'recordsets': @@ -689,9 +695,21 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => { } } useEffect(() => { + // if(search.image) { + // setResults(search.results); + // setLoading(false); + // } else { getImageOnlyResults(search); + // } }, [search]); - + // function UNSAFE_componentWillReceiveProps(nextProps){ + // if(nextProps.search.image){ + // setResults(nextProps.results) + // setLoading(false) + // }else{ + // getImageOnlyResults(nextProps.search); + // } + // } function makeImageText(data){ } From a6d5cc4574f8996533d3430c2fa70c7bd073e28a Mon Sep 17 00:00:00 2001 From: mlluciano Date: Wed, 22 May 2024 11:55:05 -0400 Subject: [PATCH 19/22] add loader to search to reduce layout shift --- app/views/base.html | 6 +++--- app/views/search.html | 11 ++++++++++- public/client/js/react/src/search.js | 8 ++++++++ public/client/js/react/src/search/results.js | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/app/views/base.html b/app/views/base.html index e7d65c99..ac540567 100644 --- a/app/views/base.html +++ b/app/views/base.html @@ -55,9 +55,9 @@
    -
    - × - Take our 30-second survey +
    + × + Take our 30-second survey
    The U.S. National Science Foundation and iDigBio are required to collect information on use of digitized collections-based specimen data. Please help us meet this requirement every time you use this search portal. Sustainability of the national digitization effort depends on evidence of data use! Maybe later.
    diff --git a/app/views/search.html b/app/views/search.html index 73162f67..9817b44b 100644 --- a/app/views/search.html +++ b/app/views/search.html @@ -10,10 +10,19 @@ + + {% endblock %} {% block content %} -
    +
    +
    {% endblock %} diff --git a/public/client/js/react/src/search.js b/public/client/js/react/src/search.js index ea5a5cc7..d1927b5d 100644 --- a/public/client/js/react/src/search.js +++ b/public/client/js/react/src/search.js @@ -16,6 +16,14 @@ const Search = () => { const [resultsTab, setResultsTab] = useState('list') const [search, setSearch] = useState(defaultSearch()) + useEffect(() => { + // Hide the loader when the component is mounted + const loader = document.getElementById('loader'); + if (loader) { + loader.style.display = 'none'; + } + }, []); + function defaultSearch(){ return { filters: defaultFilters(), diff --git a/public/client/js/react/src/search/results.js b/public/client/js/react/src/search/results.js index add8d845..7b695bb3 100644 --- a/public/client/js/react/src/search/results.js +++ b/public/client/js/react/src/search/results.js @@ -30,6 +30,7 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { let res = search.from > 0 ? results.concat(response.items) : response.items; let more = response.itemCount > (search.from + search.size); searchState.from = query.offset; + searchChange(searchState) setSearch(searchState) setResults(res); setAttribution(response.attribution); From 0c7da9a6f5b9b5e5a58f897329e99034999c4196 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Fri, 24 May 2024 14:41:43 -0400 Subject: [PATCH 20/22] optimize further with memo and useCallback --- public/client/js/react/src/search.js | 47 +++++++------ public/client/js/react/src/search/results.js | 72 +++++++++++--------- 2 files changed, 68 insertions(+), 51 deletions(-) diff --git a/public/client/js/react/src/search.js b/public/client/js/react/src/search.js index d1927b5d..4140dfcb 100644 --- a/public/client/js/react/src/search.js +++ b/public/client/js/react/src/search.js @@ -1,5 +1,5 @@ -import React, {useEffect, useState} from 'react'; +import React, {useEffect, useState, useCallback} from 'react'; import Filters, {defaultFilters, newFilterProps} from './search/filters' import Sorting, {defaultSorts} from './search/sorting' import Mapping from './search/mapping' @@ -15,13 +15,18 @@ const Search = () => { const [optionsTab, setOptionsTab] = useState('filters') const [resultsTab, setResultsTab] = useState('list') const [search, setSearch] = useState(defaultSearch()) + const [ready, setReady] = useState(false) useEffect(() => { // Hide the loader when the component is mounted const loader = document.getElementById('loader'); + const main = document.getElementById('main') if (loader) { loader.style.display = 'none'; } + if (main) { + main.style.height='auto' + } }, []); function defaultSearch(){ @@ -79,32 +84,34 @@ const Search = () => { searchHistory.push(currentSearch); // Update the state with 'search' setSearch(currentSearch) + setReady(true) }, []); - function searchChange(key,val){ + + const searchChange = useCallback((key, val) => { + console.log(key, val); var newSearch = _.cloneDeep(search); - if(typeof key == 'string'){ - newSearch[key]=val; - }else if(typeof key == 'object'){ - _.each(key,function(v,k){ - newSearch[k]=v; + if (typeof key === 'string') { + newSearch[key] = val; + } else if (typeof key === 'object') { + _.each(key, function (v, k) { + newSearch[k] = v; }); } - setSearch(newSearch) - // setHistory([...history, search]) - searchHistory.push(newSearch); - } + setSearch(newSearch); + searchHistory.push(newSearch); // Assuming searchHistory can handle reactivity properly + }, [search, setSearch, searchHistory]); - function viewChange(view,option){ - //currently only supports options panel and results tabs - if(view=='optionsTab'){ + const viewChange = useCallback((view, option) => { + if (view === 'optionsTab') { localStorage.setItem(view, option); - setOptionsTab(option) - } else if (view=='resultsTab') { + setOptionsTab(option); + } else if (view === 'resultsTab') { localStorage.setItem(view, option); - setResultsTab(option) + setResultsTab(option); } - } + }, [setOptionsTab, setResultsTab]); // Not including these in deps if they are from useState + return (
    @@ -117,7 +124,9 @@ const Search = () => {
    - + {ready && + + }
    ) diff --git a/public/client/js/react/src/search/results.js b/public/client/js/react/src/search/results.js index 7b695bb3..747ee207 100644 --- a/public/client/js/react/src/search/results.js +++ b/public/client/js/react/src/search/results.js @@ -1,9 +1,9 @@ -import React, {useState, useEffect, useMemo, useCallback, useRef} from 'react'; +import React, {useState, useEffect, useMemo, useCallback, useRef, memo} from 'react'; import idbapi from '../../../lib/idbapi'; import queryBuilder from '../../../lib/querybuilder'; -const Results = ({ searchProp, searchChange, view, viewChange }) => { +const Results = memo(({ searchProp, searchChange, view, viewChange }) => { const [lastQueryStringed, setLastQueryStringed] = useState(''); const [results, setResults] = useState([]); const [attribution, setAttribution] = useState([]); @@ -30,8 +30,8 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { let res = search.from > 0 ? results.concat(response.items) : response.items; let more = response.itemCount > (search.from + search.size); searchState.from = query.offset; - searchChange(searchState) setSearch(searchState) + // searchChange(searchState) setResults(res); setAttribution(response.attribution); setTotal(response.itemCount); @@ -43,7 +43,7 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { setLastQueryStringed(JSON.stringify(query)); } - }, [search, results, lastQueryStringed]); + }, [search, lastQueryStringed]); useEffect(() => { if (searchProp!==search) { @@ -56,7 +56,7 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { }, [search]); useEffect(() => { - const handleScroll = () => { + const handleScroll = _.debounce(() => { let newSearch = _.cloneDeep(search); if (total > newSearch.from + newSearch.size) { if (($(window).scrollTop() + 40 >= $(document).height() - $(window).height()) && (!loading)) { @@ -64,13 +64,13 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { updateResults(newSearch); } } - }; + }, 100); window.onscroll = handleScroll; return () => { window.onscroll = null; }; - }, [total, loading]); + }, [loading]); const viewChangeHandler = (event) => { event.preventDefault(); @@ -88,13 +88,13 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { let resultsComponent; switch (view) { case 'list': - resultsComponent = ; + resultsComponent = ; break; case 'labels': resultsComponent = ; break; case 'media': - resultsComponent = ; break; case 'recordsets': @@ -119,7 +119,7 @@ const Results = ({ searchProp, searchChange, view, viewChange }) => { {resultsComponent}
    ); -} +}) var sortClick=false; const ResultsList = ({search, searchChange, results, loading}) => { const [columns, setColumnsState] = useState(defaultColumns()) @@ -657,44 +657,50 @@ const ResultsLabels = ({results, loading, stamp}) => { }; -const ResultsImages = ({loadingProp, resultsProp, search}) => { +const ResultsImages = memo(({loadingProp, resultsProp, search}) => { const [results, setResults] = useState(resultsProp) const [loading, setLoading] = useState(loadingProp) - function getImageOnlyResults(search){ - - var d = new Date, self=this, searchState = _.cloneDeep(search); - searchState.image=true; + // useEffect(() => { + // setResults(resultsProp) + // }, [resultsProp]); + const getImageOnlyResults = (search) => { + console.log(search) + var d = new Date(); + var self = this; + var searchState = _.cloneDeep(search); + searchState.image = true; var query = queryBuilder.makeSearchQuery(searchState); var now = d.getTime(); let lastQueryTime = now; - setLoading(true) - idbapi.search(query,function(response){ - //make sure last query run is the last one that renders - //as responses can be out of order - if(now>= lastQueryTime){ + setLoading(true); + idbapi.search(query, function(response) { + // Make sure last query run is the last one that renders + // As responses can be out of order + if (now >= lastQueryTime) { var res; - if(searchState.from > 0){ + if (searchState.from > 0) { res = results.concat(response.items); - }else{ + } else { res = response.items; } - setResults(res) - setLoading(false) - // self.setState({results: res, loading: false},function(){ + setResults(res); + setLoading(false); + // self.setState({results: res, loading: false}, function(){ // self.forceUpdate(); // }); } }); - } + }; + function errorImage(e){ e.target.attributes['src'].value = '/portal/img/missing.svg'; } - function componentDidMount(){ - if(!search.image){ - getImageOnlyResults(search); - } - } + // function componentDidMount(){ + // if(!search.image){ + // getImageOnlyResults(search); + // } + // } useEffect(() => { // if(search.image) { // setResults(search.results); @@ -703,6 +709,7 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => { getImageOnlyResults(search); // } }, [search]); + // function UNSAFE_componentWillReceiveProps(nextProps){ // if(nextProps.search.image){ // setResults(nextProps.results) @@ -752,6 +759,7 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => { }) } }); + if(images.length === 0 && !loading){ images.push(
    @@ -774,7 +782,7 @@ const ResultsImages = ({loadingProp, resultsProp, search}) => {
    ) -}; +}); const Providers = ({attribution}) => { From 085ffbdb0c19c58b1ed99e240d7d6a159ccb7d69 Mon Sep 17 00:00:00 2001 From: mlluciano Date: Fri, 24 May 2024 14:42:57 -0400 Subject: [PATCH 21/22] update styling to further reduce cls --- app/views/base.html | 2 +- app/views/search.html | 2 +- public/client/less/idigbio.less | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/base.html b/app/views/base.html index ac540567..3640043c 100644 --- a/app/views/base.html +++ b/app/views/base.html @@ -116,7 +116,7 @@ specimen list