Repository: dhis2/capture-app
Pull request #4073: refactor: convert WidgetNote folder from Flow to TypeScript
Author: login: devin-ai-integration[bot]
Labels: name: e2e-tests
Created: 2025-05-06T11:53:00Z | Merged: 2025-05-12T11:21:58Z

# TypeScript Refactoring: WidgetNote Folder

This PR refactors the WidgetNote folder from Flow to TypeScript as part of the ongoing TypeScript migration effort.

## Changes
- Converted WidgetNote.component.js to WidgetNote.component.tsx
- Converted WidgetNote.types.js to WidgetNote.types.ts
- Converted NoteSection/NoteSection.js to NoteSection/NoteSection.tsx
- Converted index.js to index.ts
- Removed Flow directives and comments
- Added proper TypeScript types for props, state, and callbacks
- Updated Material-UI styling to use createStyles and Theme

## Testing
- Verified with `yarn tsc:check`
- Verified with `yarn linter:check`

## Link to Devin run
https://app.devin.ai/sessions/4df0d79765c64c9dab01cc37ad9cfa40

Requested by: Simona Domnisoru (sdomnisoru@dhis2.org)



Diff:
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
similarity index 79%
rename from src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
rename to src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
index 91d18f4c3e..3dc2be2b3c 100644
--- a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.js
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
@@ -1,28 +1,19 @@
-// @flow
-import React, { type ComponentType, useState, useCallback } from 'react';
+import React, { useState, useCallback, type ComponentType } from 'react';
 import i18n from '@dhis2/d2-i18n';
-import { withStyles } from '@material-ui/core';
+import { withStyles, type WithStyles } from '@material-ui/core/styles';
 import { withFocusSaver } from 'capture-ui';
 import { Parser, Editor } from '@dhis2/d2-ui-rich-text';
-import cx from 'classnames';
-import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
+import { Button, Tooltip, colors, spacersNum } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
 import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
+import type { OwnProps, NoteType } from './NoteSection.types';
 
 const FocusTextField = withFocusSaver()(TextField);
 
-type Props = {
-    notes: Array<Object>,
-    handleAddNote: (text: string) => void,
-    placeholder: string,
-    emptyNoteMessage: string,
-    ...CssClasses
-}
-
-const styles = {
+const styles: Readonly<any> = {
     item: {
         padding: spacersNum.dp12,
         marginRight: spacersNum.dp4,
@@ -72,6 +63,8 @@ const styles = {
     },
 };
 
+type Props = OwnProps & WithStyles<typeof styles>;
+
 const NoteSectionPlain = ({
     placeholder,
     emptyNoteMessage,
@@ -79,11 +72,11 @@ const NoteSectionPlain = ({
     handleAddNote,
     classes,
 }: Props) => {
-    const [isEditing, setEditing] = useState(false);
-    const [newNoteValue, setNewNoteValue] = useState('');
+    const [isEditing, setEditing] = useState<boolean>(false);
+    const [newNoteValue, setNewNoteValue] = useState<string>('');
     const { fromServerDate } = useTimeZoneConversion();
 
-    const handleChange = useCallback((value) => {
+    const handleChange = useCallback((value: string) => {
         setEditing(true);
         setNewNoteValue(value);
     }, []);
@@ -99,15 +92,15 @@ const NoteSectionPlain = ({
         setEditing(false);
     }, [handleAddNote, newNoteValue]);
 
-    const NoteItem = ({ value, storedAt, createdBy }) => (
-        <div data-test="note-item" className={cx(classes.item)}>
+    const NoteItem = ({ value, storedAt, createdBy }: NoteType) => (
+        <div data-test="note-item" className={classes.item}>
             {/* TODO: add avatar */}
-            <div className={classes.rightColumn}>
-                <div className={classes.header}>
-                    {createdBy && <span className={cx(classes.headerText, classes.name)}>
+            <div>
+                <div>
+                    {createdBy && <span className={classes.name}>
                         {createdBy.firstName} {' '} {createdBy.surname}
                     </span>}
-                    <span className={cx(classes.headerText, classes.lastUpdated)}>
+                    <span className={classes.lastUpdated}>
                         <Tooltip content={convertClientToList(moment(fromServerDate(storedAt).getClientZonedISOString()).toISOString(), dataElementTypes.DATETIME)}>
                             {moment(fromServerDate(storedAt)).fromNow()}
                         </Tooltip>
@@ -120,13 +113,12 @@ const NoteSectionPlain = ({
         </div>
     );
 
-
     return (
         <div className={classes.wrapper}>
             <div className={classes.notesWrapper}>
                 {notes
                     .sort((a, b) => moment(a.storedAt).valueOf() - moment(b.storedAt).valueOf())
-                    .map(note => <NoteItem key={`note-item-${note.note}-`} {...note} />)
+                    .map(note => <NoteItem key={`note-item-${note.note || note.value}-`} {...note} />)
                 }
                 {notes.length === 0 &&
                 <div className={classes.emptyNotes}>
@@ -163,4 +155,4 @@ const NoteSectionPlain = ({
         </div>);
 };
 
-export const NoteSection: ComponentType<Props> = withStyles(styles)(NoteSectionPlain);
+export const NoteSection = withStyles(styles)(NoteSectionPlain) as ComponentType<OwnProps>;
diff --git a/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.types.ts b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.types.ts
new file mode 100644
index 0000000000..22a6f644fd
--- /dev/null
+++ b/src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.types.ts
@@ -0,0 +1,16 @@
+export type NoteType = {
+    value: string;
+    note?: string;
+    storedAt: string;
+    createdBy?: {
+        firstName: string;
+        surname: string;
+    };
+};
+
+export type OwnProps = {
+    notes: Array<NoteType>;
+    handleAddNote: (text: string) => void;
+    placeholder: string;
+    emptyNoteMessage: string;
+};
diff --git a/src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.js b/src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.tsx
similarity index 93%
rename from src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.js
rename to src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.tsx
index 37300b12ad..0de10503f5 100644
--- a/src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.js
+++ b/src/core_modules/capture-core/components/WidgetNote/WidgetNote.component.tsx
@@ -1,4 +1,3 @@
-// @flow
 import React, { useState, useCallback } from 'react';
 import { Chip } from '@dhis2/ui';
 import { Widget } from '../Widget';
@@ -6,7 +5,7 @@ import type { Props } from './WidgetNote.types';
 import { NoteSection } from './NoteSection/NoteSection';
 
 export const WidgetNote = ({ title, notes, onAddNote, ...passOnProps }: Props) => {
-    const [open, setOpenStatus] = useState(true);
+    const [open, setOpenStatus] = useState<boolean>(true);
 
     return (
         <Widget
@@ -26,6 +25,5 @@ export const WidgetNote = ({ title, notes, onAddNote, ...passOnProps }: Props) =
                 {...passOnProps}
             />
         </Widget>
-
     );
 };
diff --git a/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.js b/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.js
deleted file mode 100644
index d005a1b4aa..0000000000
--- a/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// @flow
-
-export type Props = {|
-    title: string,
-    placeholder: string,
-    emptyNoteMessage: string,
-    notes: Array<Object>,
-    onAddNote: (note: string) => void,
-    ...CssClasses
-|};
diff --git a/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.ts b/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.ts
new file mode 100644
index 0000000000..e7be709c8a
--- /dev/null
+++ b/src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.ts
@@ -0,0 +1,14 @@
+export type Props = {
+    title: string;
+    placeholder: string;
+    emptyNoteMessage: string;
+    notes: Array<{
+        value: string;
+        storedAt: string;
+        createdBy?: {
+            firstName: string;
+            surname: string;
+        };
+    }>;
+    onAddNote: (note: string) => void;
+};
diff --git a/src/core_modules/capture-core/components/WidgetNote/index.js b/src/core_modules/capture-core/components/WidgetNote/index.ts
similarity index 100%
rename from src/core_modules/capture-core/components/WidgetNote/index.js
rename to src/core_modules/capture-core/components/WidgetNote/index.ts


Reviews:
user: login: simonadomnisoru
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T07:24:55Z
user: login: simonadomnisoru
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:07:09Z
user: login: simonadomnisoru
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T07:45:27Z
user: login: simonadomnisoru
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T08:05:47Z
user: login: simonadomnisoru
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T09:57:48Z
user: login: simonadomnisoru
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T11:21:35Z

Review comments:
diff_hunk: @@ -1,7 +1,6 @@
-// @flow
-import React, { type ComponentType, useState, useCallback } from 'react';
+import React, { useState, useCallback, type ComponentType } from 'react';
 import i18n from '@dhis2/d2-i18n';
-import { withStyles } from '@material-ui/core';
+import { withStyles, createStyles, type WithStyles } from '@material-ui/core';
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Rollback createStyles. You do not need to add it in the future
created_at: 2025-05-07T06:42:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,18 +10,11 @@ import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
 import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
+import type { NoteSectionProps, NoteType } from '../WidgetNote.types';
 
 const FocusTextField = withFocusSaver()(TextField);
 
-type Props = {
-    notes: Array<Object>,
-    handleAddNote: (text: string) => void,
-    placeholder: string,
-    emptyNoteMessage: string,
-    ...CssClasses
-}
-
-const styles = {
+const getStyles = () => createStyles({
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Rollback `createStyles` and `getStyles`. Follow the pattern from src/core_modules/capture-core/components/WidgetFeedback/WidgetFeedbackContent/WidgetFeedbackContent.tsx
created_at: 2025-05-07T06:42:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,24 @@
+export type NoteType = {
+    value: string;
+    storedAt: string;
+    createdBy?: {
+        firstName: string;
+        surname: string;
+    };
+};
+
+export type NoteSectionProps = {
+    notes: Array<NoteType>;
+    handleAddNote: (text: string) => void;
+    placeholder: string;
+    emptyNoteMessage: string;
+    classes?: Record<string, string>;
+};
path: src/core_modules/capture-core/components/WidgetNote/WidgetNote.types.ts
user: login: simonadomnisoru
body: Create a new file `NoteSection.types.ts` in the `/NoteSection` folder and move these types definitions
created_at: 2025-05-07T07:23:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,2 +1,3 @@
 export { NoteSection } from './NoteSection/NoteSection';
 export { WidgetNote } from './WidgetNote.component';
+export type { Props, NoteType, NoteSectionProps } from './WidgetNote.types';
path: src/core_modules/capture-core/components/WidgetNote/index.ts
user: login: simonadomnisoru
body: Roll back this change. The types are not needed outside WidgetNote
created_at: 2025-05-07T07:24:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,89 +1,33 @@
-// @flow
-import React, { type ComponentType, useState, useCallback } from 'react';
+import React, { useState, useCallback, type ComponentType } from 'react';
 import i18n from '@dhis2/d2-i18n';
-import { withStyles } from '@material-ui/core';
+import { withStyles, type WithStyles } from '@material-ui/core';
 import { withFocusSaver } from 'capture-ui';
 import { Parser, Editor } from '@dhis2/d2-ui-rich-text';
 import cx from 'classnames';
-import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
+import { Button, Tooltip } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
 import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
+import { styles, type Props, type NoteType } from './NoteSection.types';
 
 const FocusTextField = withFocusSaver()(TextField);
 
-type Props = {
-    notes: Array<Object>,
-    handleAddNote: (text: string) => void,
-    placeholder: string,
-    emptyNoteMessage: string,
-    ...CssClasses
-}
-
-const styles = {
-    item: {
-        padding: spacersNum.dp12,
-        marginRight: spacersNum.dp4,
-        background: colors.grey200,
-        borderRadius: '5px',
-        display: 'flex',
-        fontSize: '14px',
-        lineHeight: '19px',
-        color: colors.grey900,
-        '& + &': {
-            marginTop: spacersNum.dp8,
-        },
-    },
-    wrapper: {
-        padding: `0 ${spacersNum.dp16}px`,
-        marginBottom: spacersNum.dp16,
-    },
-    notesWrapper: {
-        maxHeight: 400,
-        overflowY: 'auto',
-    },
-    editor: {
-        paddingTop: spacersNum.dp16,
-    },
-    emptyNotes: {
-        fontSize: 14,
-        color: colors.grey600,
-    },
-    name: {
-        fontSize: '13px',
-        fontWeight: 500,
-    },
-    lastUpdated: {
-        fontSize: '12px',
-        marginLeft: '8px',
-        color: colors.grey700,
-    },
-    body: {
-        '& p': {
-            margin: `${spacersNum.dp8}px 0 0 0`,
-        },
-    },
-    newNoteButtonContainer: {
-        paddingTop: spacersNum.dp4,
-        display: 'flex',
-        gap: '4px',
-    },
-};
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Rollback this change. Move the `styles` from NoteSection.types.ts to NoteSection.tsx
created_at: 2025-05-12T06:44:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,89 +1,33 @@
-// @flow
-import React, { type ComponentType, useState, useCallback } from 'react';
+import React, { useState, useCallback, type ComponentType } from 'react';
 import i18n from '@dhis2/d2-i18n';
-import { withStyles } from '@material-ui/core';
+import { withStyles, type WithStyles } from '@material-ui/core';
 import { withFocusSaver } from 'capture-ui';
 import { Parser, Editor } from '@dhis2/d2-ui-rich-text';
 import cx from 'classnames';
-import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
+import { Button, Tooltip } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
 import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
+import { styles, type Props, type NoteType } from './NoteSection.types';
 
 const FocusTextField = withFocusSaver()(TextField);
 
-type Props = {
-    notes: Array<Object>,
-    handleAddNote: (text: string) => void,
-    placeholder: string,
-    emptyNoteMessage: string,
-    ...CssClasses
-}
-
-const styles = {
-    item: {
-        padding: spacersNum.dp12,
-        marginRight: spacersNum.dp4,
-        background: colors.grey200,
-        borderRadius: '5px',
-        display: 'flex',
-        fontSize: '14px',
-        lineHeight: '19px',
-        color: colors.grey900,
-        '& + &': {
-            marginTop: spacersNum.dp8,
-        },
-    },
-    wrapper: {
-        padding: `0 ${spacersNum.dp16}px`,
-        marginBottom: spacersNum.dp16,
-    },
-    notesWrapper: {
-        maxHeight: 400,
-        overflowY: 'auto',
-    },
-    editor: {
-        paddingTop: spacersNum.dp16,
-    },
-    emptyNotes: {
-        fontSize: 14,
-        color: colors.grey600,
-    },
-    name: {
-        fontSize: '13px',
-        fontWeight: 500,
-    },
-    lastUpdated: {
-        fontSize: '12px',
-        marginLeft: '8px',
-        color: colors.grey700,
-    },
-    body: {
-        '& p': {
-            margin: `${spacersNum.dp8}px 0 0 0`,
-        },
-    },
-    newNoteButtonContainer: {
-        paddingTop: spacersNum.dp4,
-        display: 'flex',
-        gap: '4px',
-    },
-};
+type PropsWithStyles = Props & WithStyles<typeof styles>;
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Change to `type Props = OwnProps & WithStyles<typeof styles>;` and rename `Props` to `OwnProps` in NoteSection.types
created_at: 2025-05-12T06:48:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -120,13 +64,12 @@ const NoteSectionPlain = ({
         </div>
     );
 
-
     return (
         <div className={classes.wrapper}>
             <div className={classes.notesWrapper}>
                 {notes
                     .sort((a, b) => moment(a.storedAt).valueOf() - moment(b.storedAt).valueOf())
-                    .map(note => <NoteItem key={`note-item-${note.note}-`} {...note} />)
+                    .map(note => <NoteItem key={`note-item-${note.value}-`} {...note} />)
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Use `note-item-${note.value || note.value}-`
created_at: 2025-05-12T06:59:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,77 @@
+import { colors, spacersNum } from '@dhis2/ui';
+
+export type NoteType = {
+    value: string;
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.types.ts
user: login: simonadomnisoru
body: Add `note?: string;`
created_at: 2025-05-12T07:00:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,7 +34,7 @@ const styles = {
     },
     notesWrapper: {
         maxHeight: 400,
-        overflowY: 'auto',
+        overflowY: 'auto' as const,
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Remove `const` and rollback to `overflowY: 'auto',`
created_at: 2025-05-12T07:31:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -114,19 +118,18 @@ const NoteSectionPlain = ({
                     </span>
                 </div>
                 <div className={classes.body}>
-                    <Parser>{value}</Parser>
+                    <Parser>{note || value}</Parser>
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Rollback to `<Parser>{value}</Parser>`
created_at: 2025-05-12T07:32:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -99,7 +103,7 @@ const NoteSectionPlain = ({
         setEditing(false);
     }, [handleAddNote, newNoteValue]);
 
-    const NoteItem = ({ value, storedAt, createdBy }) => (
+    const NoteItem = ({ value, note, storedAt, createdBy }: NoteType) => (
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Rollback to `const NoteItem = ({ value, storedAt, createdBy }) => (`
created_at: 2025-05-12T07:32:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -114,19 +118,18 @@ const NoteSectionPlain = ({
                     </span>
                 </div>
                 <div className={classes.body}>
-                    <Parser>{value}</Parser>
+                    <Parser>{note || value}</Parser>
                 </div>
             </div>
         </div>
     );
 
-
     return (
         <div className={classes.wrapper}>
             <div className={classes.notesWrapper}>
                 {notes
                     .sort((a, b) => moment(a.storedAt).valueOf() - moment(b.storedAt).valueOf())
-                    .map(note => <NoteItem key={`note-item-${note.note}-`} {...note} />)
+                    .map(note => <NoteItem key={`note-item-${note.value || note.note}-`} {...note} />)
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: First use `note.note` as the default and `note.value` as the fallback. Like this `{note.note || note.value}`
created_at: 2025-05-12T07:34:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,20 +62,32 @@ const styles = {
         display: 'flex',
         gap: '4px',
     },
+    rightColumn: {
+        flex: 1,
+    },
+    header: {
+        display: 'flex',
+        alignItems: 'center',
+    },
+    headerText: {
+        display: 'inline-block',
+    },
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Do not add new styles. Rollback these lines.
created_at: 2025-05-12T07:39:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -99,7 +103,7 @@ const NoteSectionPlain = ({
         setEditing(false);
     }, [handleAddNote, newNoteValue]);
 
-    const NoteItem = ({ value, storedAt, createdBy }) => (
+    const NoteItem = ({ value, note, storedAt, createdBy }: NoteType) => (
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Clean up classes that do not exist in styles. The `NoteItem` should look like this:
```
      const NoteItem = ({ value, storedAt, createdBy }) => (
        <div data-test="note-item" className={classes.item}>
            {/* TODO: add avatar */}
            <div>
                <div>
                    {createdBy && <span className={classes.name}>
                        {createdBy.firstName} {' '} {createdBy.surname}
                    </span>}
                    <span className={classes.lastUpdated}>
                        <Tooltip content={convertClientToList(moment(fromServerDate(storedAt).getClientZonedISOString()).toISOString(), dataElementTypes.DATETIME)}>
                            {moment(fromServerDate(storedAt)).fromNow()}
                        </Tooltip>
                    </span>
                </div>
                <div className={classes.body}>
                    <Parser>{value}</Parser>
                </div>
            </div>
        </div>
    );
```
created_at: 2025-05-12T07:41:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,28 +1,19 @@
-// @flow
-import React, { type ComponentType, useState, useCallback } from 'react';
+import React, { useState, useCallback, type ComponentType } from 'react';
 import i18n from '@dhis2/d2-i18n';
-import { withStyles } from '@material-ui/core';
+import { withStyles, type WithStyles, createStyles } from '@material-ui/core';
 import { withFocusSaver } from 'capture-ui';
 import { Parser, Editor } from '@dhis2/d2-ui-rich-text';
-import cx from 'classnames';
-import { colors, spacersNum, Button, Tooltip } from '@dhis2/ui';
+import { Button, Tooltip, colors, spacersNum } from '@dhis2/ui';
 import moment from 'moment';
 import { useTimeZoneConversion } from '@dhis2/app-runtime';
 import { TextField } from '../../FormFields/New';
 import { convertClientToList } from '../../../converters';
 import { dataElementTypes } from '../../../metaData';
+import type { OwnProps } from './NoteSection.types';
 
 const FocusTextField = withFocusSaver()(TextField);
 
-type Props = {
-    notes: Array<Object>,
-    handleAddNote: (text: string) => void,
-    placeholder: string,
-    emptyNoteMessage: string,
-    ...CssClasses
-}
-
-const styles = {
+const styles = createStyles({
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: There's no need to import or use `createStyles`. I mentioned this in a previous comment: https://github.com/dhis2/capture-app/pull/4073#discussion_r2076903997. Please keep this in mind for your upcoming pull requests.
created_at: 2025-05-12T07:57:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -120,14 +113,22 @@ const NoteSectionPlain = ({
         </div>
     );
 
-
     return (
         <div className={classes.wrapper}>
             <div className={classes.notesWrapper}>
                 {notes
                     .sort((a, b) => moment(a.storedAt).valueOf() - moment(b.storedAt).valueOf())
-                    .map(note => <NoteItem key={`note-item-${note.note}-`} {...note} />)
-                }
+                    .map((note) => {
+                        const noteValue = note.note || note.value;
+                        return (
+                            <NoteItem
+                                key={`note-item-${noteValue}-`}
+                                value={noteValue}
+                                storedAt={note.storedAt}
+                                createdBy={note.createdBy}
+                            />
+                        );
+                    })}
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: Use this exact line instead: '.map(note => <NoteItem key={`note-item-${note.note || note.value}-`} {...note} />)'
created_at: 2025-05-12T08:02:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -42,7 +33,7 @@ const styles = {
     },
     notesWrapper: {
         maxHeight: 400,
-        overflowY: 'auto',
+        overflowY: 'auto' as const,
path: src/core_modules/capture-core/components/WidgetNote/NoteSection/NoteSection.tsx
user: login: simonadomnisoru
body: It looks like you added `as const` to fix the error `Type 'string' is not assignable to type 'OverflowYProperty | undefined'`. Instead, I want to fix this error by using `Readonly` for styles. The fix should look like this: `const style: Readonly<any>`
created_at: 2025-05-12T09:54:31Z
author_association: CONTRIBUTOR

PR comments:
user: login: devin-ai-integration[bot]
created_at: 2025-05-06T11:53:04Z
body: ### 🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:
- Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
- Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:
- [ ] Disable automatic comment and CI monitoring

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-12T10:40:19Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=dhis2_capture-app&pullRequest=4073) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=dhis2_capture-app&pullRequest=4073&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [5 New Code Smells](https://sonarcloud.io/dashboard?id=dhis2_capture-app&pullRequest=4073) (required ≤ 0)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=dhis2_capture-app&pullRequest=4073)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


author_association: NONE