From 9cd9f0d49a3a0fdd2804aef3d148627213ff74f2 Mon Sep 17 00:00:00 2001 From: Anto <“mrap1988@gmail.com”> Date: Tue, 2 May 2023 13:55:39 +0200 Subject: [PATCH 1/2] SKCAN-85 feat: implement changes to the autosave SCKAN-85 disable autosave and add save onChange for dropdowns SCKAN-85 feat: disable autosave and add saving onblur and onchange SCKAN-85 implement autosave every 2 min for text fields- --- frontend/src/Pages/SentenceDetails.tsx | 1 + frontend/src/components/DistillationTab.tsx | 1 + frontend/src/components/Forms/FormBase.tsx | 52 +++++++++++++++++-- .../src/components/Forms/SentenceAddForm.tsx | 2 +- .../src/components/Forms/SentenceForm.tsx | 6 +-- .../src/components/Forms/StatementForm.tsx | 16 +++--- .../components/ProofingTab/PathsBuilder.tsx | 6 +++ .../components/ProofingTab/ProofingTab.tsx | 5 +- .../components/StatementWithProvenances.tsx | 1 + .../StatementDetailsAccordion.tsx | 1 + .../TriageStatementSection.tsx | 1 + .../src/components/Widgets/CustomTextArea.tsx | 35 ++----------- .../components/Widgets/CustomTextField.tsx | 8 ++- frontend/src/settings.ts | 2 +- 14 files changed, 85 insertions(+), 52 deletions(-) diff --git a/frontend/src/Pages/SentenceDetails.tsx b/frontend/src/Pages/SentenceDetails.tsx index fae3aaff..8d16e195 100644 --- a/frontend/src/Pages/SentenceDetails.tsx +++ b/frontend/src/Pages/SentenceDetails.tsx @@ -213,6 +213,7 @@ const SentencesDetails = () => { disabled={disabled} format="small" setter={setSentence} + enableAutoSave={true} /> diff --git a/frontend/src/components/DistillationTab.tsx b/frontend/src/components/DistillationTab.tsx index e5e2c4e2..6b859f92 100644 --- a/frontend/src/components/DistillationTab.tsx +++ b/frontend/src/components/DistillationTab.tsx @@ -48,6 +48,7 @@ const DistillationTab = ({ statement, setStatement, refreshStatement, disabled } statement_id: statement.id, knowledge_statement: statement.knowledge_statement, }} + enableAutoSave={true} uiFields={[ "sex_id", "apinatomy_model", diff --git a/frontend/src/components/Forms/FormBase.tsx b/frontend/src/components/Forms/FormBase.tsx index 30207f62..c4fa57f5 100644 --- a/frontend/src/components/Forms/FormBase.tsx +++ b/frontend/src/components/Forms/FormBase.tsx @@ -30,7 +30,9 @@ export const FormBase = (props: any) => { templates, submitButtonProps, className = false, - showErrorList + showErrorList, + submitOnChangeFields = [], + submitOnBlurFields = [] } = props; const [localData, setLocalData] = useState(data); const [isSaving, setIsSaving] = useState(false); @@ -39,6 +41,9 @@ export const FormBase = (props: any) => { useState(false); const [customSchema, setCustomSchema] = useState(schema); const [customUiSchema, setCustomUiSchema] = useState(uiSchema); + const [timer, setTimer] = useState(0) + + const interval = useRef(null) const submitButtonRef = useRef(null); const removeProp = (obj: any, prop: string) => { @@ -68,6 +73,25 @@ export const FormBase = (props: any) => { } }, [data]); + const startTimer = () => interval.current = setInterval(()=>setTimer((timer)=>timer + 1000), 1000) + const stopTimer = () =>{ + clearInterval(interval.current) + setTimer(0) + } + + + useEffect(() => { + if(timer === EDIT_DEBOUNCE && enableAutoSave){ + onSave() + setTimer(0) + } + }, [timer]) + + useEffect(() => { + return () => stopTimer() + }, []) + + const onError = (errors: any) => { log("errors"); log(errors); @@ -105,19 +129,35 @@ export const FormBase = (props: any) => { }); }; - const handleUpdate = async (event: IChangeEvent) => { + const handleUpdate = async (event: IChangeEvent, id: any) => { const formData = { ...event.formData, ...extraData }; + if(submitOnBlurFields.some((field:string)=> id.includes(field))){ + setTimer(0) + } + if(submitOnChangeFields.some((field:string)=> id.includes(field))){ + return onSave() + } setLocalData(formData); if (formIsValid && !formIsValid(formData)) { setIsSubmitButtonDisabled(true); } else { setIsSubmitButtonDisabled(false); - if (enableAutoSave) { - return triggerAutoSave(); - } } }; + const handleBlur = async (id: string) => { + if(submitOnBlurFields.some((field:string)=> id.includes(field))){ + stopTimer() + return onSave() + } + } + + const handleFocus = (id: string) => { + if(submitOnBlurFields.some((field:string)=> id.includes(field))){ + startTimer() + } + } + return ( <> {(!data || isSaving) && ( @@ -135,6 +175,8 @@ export const FormBase = (props: any) => { onChange={handleUpdate} onSubmit={handleSubmit} onError={onError} + onBlur={handleBlur} + onFocus={handleFocus} widgets={widgets} className={className} templates={templates} diff --git a/frontend/src/components/Forms/SentenceAddForm.tsx b/frontend/src/components/Forms/SentenceAddForm.tsx index 526c4fcc..e7e4c8a2 100644 --- a/frontend/src/components/Forms/SentenceAddForm.tsx +++ b/frontend/src/components/Forms/SentenceAddForm.tsx @@ -87,7 +87,7 @@ const SentenceAddForm = (props: any) => { schema={customSchema} uiSchema={customUiSchema} uiFields={uiFields} - enableAutoSave={true} + enableAutoSave={false} formIsValid={format === "create" && formIsValid} disableSubmitButton={setDisabled} submitButtonProps={submitButtonProps} diff --git a/frontend/src/components/Forms/SentenceForm.tsx b/frontend/src/components/Forms/SentenceForm.tsx index 5a3a3918..fbafdbbc 100644 --- a/frontend/src/components/Forms/SentenceForm.tsx +++ b/frontend/src/components/Forms/SentenceForm.tsx @@ -98,14 +98,12 @@ const SentenceForm = (props: any) => { schema={customSchema} uiSchema={customUiSchema} uiFields={uiFields} - enableAutoSave={true} + enableAutoSave={false} children={true} + submitOnBlurFields={['title']} {...props} /> - {/* - PMID (PubMed identifier) - */} { linkedChip({id: data?.pmid, uri: data?.pmid_uri, label: "PMID"}) } diff --git a/frontend/src/components/Forms/StatementForm.tsx b/frontend/src/components/Forms/StatementForm.tsx index 37e74b00..dcd89ee1 100644 --- a/frontend/src/components/Forms/StatementForm.tsx +++ b/frontend/src/components/Forms/StatementForm.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { FormBase } from "./FormBase"; import { jsonSchemas } from "../../services/JsonSchema"; import statementService from "../../services/StatementService"; @@ -10,6 +10,7 @@ import AnatomicalEntitiesField from "../AnatomicalEntitiesField"; import { sexes } from '../../services/SexService'; import { phenotypes } from '../../services/PhenotypeService'; + const StatementForm = (props: any) => { const { uiFields, statement, setter, format } = props; const { schema, uiSchema } = jsonSchemas.getConnectivityStatementSchema(); @@ -37,7 +38,7 @@ const StatementForm = (props: any) => { copiedUISchema.projection= { "ui:widget": "radio", "ui:options": { - classNames: 'col-xs-12 col-md-6' + classNames: 'col-xs-12 col-md-6', } } @@ -67,7 +68,7 @@ const StatementForm = (props: any) => { placeholder: "Select Phenotype", data: phenotypes.getPhenotypes().map((row: any) => ({ label: row.name, value: row.id })), }, - value: statement?.phenotype_id ?? "" + value: statement?.phenotype_id ?? "", } copiedUISchema.knowledge_statement = { @@ -76,7 +77,6 @@ const StatementForm = (props: any) => { label: "Knowledge Statement", placeholder: "Enter Knowledge Statement", rows: 4, - hasDebouncedOnChange: true, value: statement?.knowledge_statement ?? "", }, }; @@ -127,7 +127,7 @@ const StatementForm = (props: any) => { label: "Additional Information", placeholder: "Enter additional information on the knowledge statement", multiline: true, - rows: 4 + rows: 4, }, value: statement?.additional_information ?? "", }; @@ -146,17 +146,19 @@ const StatementForm = (props: any) => { return ( ); }; diff --git a/frontend/src/components/ProofingTab/PathsBuilder.tsx b/frontend/src/components/ProofingTab/PathsBuilder.tsx index 26678948..39f7168d 100644 --- a/frontend/src/components/ProofingTab/PathsBuilder.tsx +++ b/frontend/src/components/ProofingTab/PathsBuilder.tsx @@ -51,6 +51,8 @@ const PathsBuilder = (props: any) => { setter={refreshStatement} extraData={{ sentence_id: statement.sentence.id }} uiFields={["origin_id"]} + enableAutoSave={false} + submitOnChangeFields={['origin_id']} /> @@ -68,6 +70,8 @@ const PathsBuilder = (props: any) => { extraData={{ sentence_id: statement.sentence.id }} uiFields={["path"]} className="vias" + enableAutoSave={false} + submitOnChangeFields={['path']} /> @@ -84,6 +88,8 @@ const PathsBuilder = (props: any) => { extraData={{ sentence_id: statement.sentence.id }} uiFields={["destination_id", "destination_type"]} className="inLineForm" + enableAutoSave={false} + submitOnChangeFields={["destination_id", "destination_type"]} /> diff --git a/frontend/src/components/ProofingTab/ProofingTab.tsx b/frontend/src/components/ProofingTab/ProofingTab.tsx index 495bd9a9..374a9ea6 100644 --- a/frontend/src/components/ProofingTab/ProofingTab.tsx +++ b/frontend/src/components/ProofingTab/ProofingTab.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { ConnectivityStatement } from "../../apiclient/backend/api"; import { Grid, Typography, Box, Stack, Divider, Paper } from "@mui/material"; import StatementChart from "./StatementChart"; import StatementWithProvenances from "../StatementWithProvenances"; @@ -9,7 +8,7 @@ import { useTheme } from "@mui/system"; import PathsBuilder from "./PathsBuilder"; const ProofingTab = (props: any) => { - const { statement } = props; + const { statement, refreshStatement, setStatement } = props; const theme = useTheme(); const sectionStyle = useSectionStyle(theme); @@ -42,7 +41,7 @@ const ProofingTab = (props: any) => { textAlign: "center", }} > - + diff --git a/frontend/src/components/StatementWithProvenances.tsx b/frontend/src/components/StatementWithProvenances.tsx index 3fcf70e4..8066b71a 100644 --- a/frontend/src/components/StatementWithProvenances.tsx +++ b/frontend/src/components/StatementWithProvenances.tsx @@ -26,6 +26,7 @@ const StatementWithProvenances = ({ statement, background = "#fff", refreshState uiFields={["knowledge_statement"]} className='ks' disabled={disabled} + enableAutoSave={true} /> { statement={statement} format="small" setter={setter} + enableAutoSave={true} extraData={{ sentence_id: sentence.id, knowledge_statement: statement.knowledge_statement, diff --git a/frontend/src/components/TriageStatementSection/TriageStatementSection.tsx b/frontend/src/components/TriageStatementSection/TriageStatementSection.tsx index e3462d9a..fbc923b9 100644 --- a/frontend/src/components/TriageStatementSection/TriageStatementSection.tsx +++ b/frontend/src/components/TriageStatementSection/TriageStatementSection.tsx @@ -39,6 +39,7 @@ const TriageStatementSection = (props: any) => { extraData={{ sentence_id: sentence.id }} uiFields={["knowledge_statement"]} className='ks' + enableAutoSave={true} /> ({ @@ -22,36 +20,12 @@ const StyledInput = styled(TextField)(({ theme }) => ({ })); -export default function TextArea({value, placeholder, required, disabled, onChange, options: { rows, hasDebouncedOnChange } }: any) { +export default function TextArea({ id, value, placeholder, required, disabled, onChange, onBlur, onFocus, options: { rows } }: any) { - const [inputValue, setInputValue] = React.useState(value) - - const debouncedChangeHandler = useDebouncedCallback( - (event) => onChange(event.target.value), - EDIT_DEBOUNCE - ); - - const handleChange = (e: React.ChangeEvent) => { - const inputValue = e.target.value; - setInputValue(inputValue); - debouncedChangeHandler(e); - }; - - React.useEffect(()=>setInputValue(value), [value]) return ( - {hasDebouncedOnChange - ? - :onChange(e.target.value)} + onBlur={(e)=>onBlur(id,e.target.value)} + onFocus={(e)=>onFocus(id,e.target.value)} + disabled={disabled} /> - } - ); } diff --git a/frontend/src/components/Widgets/CustomTextField.tsx b/frontend/src/components/Widgets/CustomTextField.tsx index da04e73e..f3242232 100644 --- a/frontend/src/components/Widgets/CustomTextField.tsx +++ b/frontend/src/components/Widgets/CustomTextField.tsx @@ -1,6 +1,5 @@ import * as React from "react"; import TextField from "@mui/material/TextField"; -import InputLabel from "@mui/material/InputLabel"; import FormControl from "@mui/material/FormControl"; import { styled } from "@mui/material"; import Typography from "@mui/material/Typography"; @@ -16,12 +15,17 @@ const StyledInput = styled(TextField)(({ theme }) => ({ })); export default function CustomTextField({ + id, value, placeholder, disabled, onChange, + onBlur, + onFocus, options: { label, multiline, rows }, }: any) { + + return ( @@ -36,6 +40,8 @@ export default function CustomTextField({ rows={rows} value={value ? value : ''} disabled={disabled} + onBlur={(e=>onBlur(id,e.target.value))} + onFocus={(e=>onFocus(id,e.target.value))} /> ); diff --git a/frontend/src/settings.ts b/frontend/src/settings.ts index 598a7a64..a0e25e9d 100644 --- a/frontend/src/settings.ts +++ b/frontend/src/settings.ts @@ -1,3 +1,3 @@ -export const EDIT_DEBOUNCE = 1500 // 1.5 seconds +export const EDIT_DEBOUNCE = 120000 // 120 seconds export const SEARCH_DEBOUNCE = 300 // 0.3 seconds export const ROWS_PER_PAGE = 10 //number of records displayed at sentence and statement list From 0b13f89f73b90d5e017d251c75fb5d5eac43a2ed Mon Sep 17 00:00:00 2001 From: Anto <“mrap1988@gmail.com”> Date: Wed, 3 May 2023 15:41:54 +0200 Subject: [PATCH 2/2] replace setInterval by setTimeout --- frontend/src/components/Forms/FormBase.tsx | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/Forms/FormBase.tsx b/frontend/src/components/Forms/FormBase.tsx index c4fa57f5..1830011e 100644 --- a/frontend/src/components/Forms/FormBase.tsx +++ b/frontend/src/components/Forms/FormBase.tsx @@ -41,9 +41,8 @@ export const FormBase = (props: any) => { useState(false); const [customSchema, setCustomSchema] = useState(schema); const [customUiSchema, setCustomUiSchema] = useState(uiSchema); - const [timer, setTimer] = useState(0) - const interval = useRef(null) + const timer = useRef(null) const submitButtonRef = useRef(null); const removeProp = (obj: any, prop: string) => { @@ -73,19 +72,21 @@ export const FormBase = (props: any) => { } }, [data]); - const startTimer = () => interval.current = setInterval(()=>setTimer((timer)=>timer + 1000), 1000) + const startTimer = () => timer.current = setTimeout(()=>{ + if(enableAutoSave){ + onSave() + } + },EDIT_DEBOUNCE) + const stopTimer = () =>{ - clearInterval(interval.current) - setTimer(0) + clearTimeout(timer.current) } + const resetTimer = () =>{ + stopTimer() + startTimer() + } - useEffect(() => { - if(timer === EDIT_DEBOUNCE && enableAutoSave){ - onSave() - setTimer(0) - } - }, [timer]) useEffect(() => { return () => stopTimer() @@ -132,7 +133,7 @@ export const FormBase = (props: any) => { const handleUpdate = async (event: IChangeEvent, id: any) => { const formData = { ...event.formData, ...extraData }; if(submitOnBlurFields.some((field:string)=> id.includes(field))){ - setTimer(0) + resetTimer() } if(submitOnChangeFields.some((field:string)=> id.includes(field))){ return onSave()