diff --git a/i18n/en.pot b/i18n/en.pot index eef4b4f9..76f0144c 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-07-01T02:04:44.570Z\n" -"PO-Revision-Date: 2024-07-01T02:04:44.570Z\n" +"POT-Creation-Date: 2024-08-05T08:04:28.112Z\n" +"PO-Revision-Date: 2024-08-05T08:04:28.112Z\n" msgid "Low" msgstr "" @@ -32,10 +32,14 @@ msgstr "" msgid "Within a province with more than one district affected" msgstr "" -msgid "More than one province affected with high threat of spread locally and internationally" +msgid "" +"More than one province affected with high threat of spread locally and " +"internationally" msgstr "" -msgid "Available within the district with support from provincial and national level" +msgid "" +"Available within the district with support from provincial and national " +"level" msgstr "" msgid "Available within the province with minimal support from national level" @@ -62,10 +66,10 @@ msgstr "" msgid "Indicates required" msgstr "" -msgid "Save" +msgid "Cancel" msgstr "" -msgid "Cancel" +msgid "Save" msgstr "" msgid "Create Event" @@ -80,6 +84,15 @@ msgstr "" msgid "Last updated: " msgstr "" +msgid "Median" +msgstr "" + +msgid "% Target Met" +msgstr "" + +msgid "Status: " +msgstr "" + msgid "Dashboard" msgstr "" @@ -89,6 +102,12 @@ msgstr "" msgid "Event Tracker" msgstr "" +msgid "Page Not Found" +msgstr "" + +msgid "Create Event form cannot be loaded" +msgstr "" + msgid "Incident Action Plan" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 2dc83d3b..d711cc9c 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-07-01T02:04:44.570Z\n" +"POT-Creation-Date: 2024-08-05T08:04:28.112Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -65,10 +65,10 @@ msgstr "" msgid "Indicates required" msgstr "" -msgid "Save" +msgid "Cancel" msgstr "" -msgid "Cancel" +msgid "Save" msgstr "" msgid "Create Event" @@ -83,6 +83,15 @@ msgstr "" msgid "Last updated: " msgstr "" +msgid "Median" +msgstr "" + +msgid "% Target Met" +msgstr "" + +msgid "Status: " +msgstr "" + msgid "Dashboard" msgstr "" @@ -92,6 +101,12 @@ msgstr "" msgid "Event Tracker" msgstr "" +msgid "Page Not Found" +msgstr "" + +msgid "Create Event form cannot be loaded" +msgstr "" + msgid "Incident Action Plan" msgstr "" diff --git a/package.json b/package.json index e01162d7..162aa94a 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react-dom": "^18.2.0", "react-router-dom": "5.2.0", "real-cancellable-promise": "^1.1.2", + "string-ts": "2.2.0", "styled-components": "5.3.5", "styled-jsx": "3.4.5", "typed-immutable-map": "^0.1.1", @@ -76,7 +77,7 @@ "ts-node": "^10.9.1", "ts-prune": "^0.10.3", "typedoc": "^0.25.1", - "typescript": "5.2.2", + "typescript": "5.3.2", "vite": "^4.2.0", "vite-bundle-visualizer": "^0.6.0", "vite-plugin-checker": "^0.6.2", diff --git a/src/utils/tests.tsx b/src/utils/tests.tsx index b41cd9cb..ba4d1938 100644 --- a/src/utils/tests.tsx +++ b/src/utils/tests.tsx @@ -4,6 +4,11 @@ import { ReactNode } from "react"; import { AppContext, AppContextState } from "../webapp/contexts/app-context"; import { getTestCompositionRoot } from "../CompositionRoot"; import { createAdminUser } from "../domain/entities/__tests__/userFixtures"; +import { MuiThemeProvider } from "@material-ui/core"; +import { ThemeProvider } from "styled-components"; +import OldMuiThemeProvider from "material-ui/styles/MuiThemeProvider"; +import muiThemeLegacy from "../webapp/pages/app/themes/dhis2-legacy.theme"; +import { muiTheme } from "../webapp/pages/app/themes/dhis2.theme"; export function getTestContext() { const context: AppContextState = { @@ -18,8 +23,14 @@ export function getReactComponent(children: ReactNode): RenderResult { const context = getTestContext(); return render( - - {children} - + + + + + {children} + + + + ); } diff --git a/src/webapp/components/checkbox/Checkbox.tsx b/src/webapp/components/checkbox/Checkbox.tsx index 47634f61..2b686cc0 100644 --- a/src/webapp/components/checkbox/Checkbox.tsx +++ b/src/webapp/components/checkbox/Checkbox.tsx @@ -24,6 +24,9 @@ export const Checkbox: React.FC = React.memo( helperText = "", disabled = false, indeterminate = false, + errorText = "", + error = false, + required = false, }) => { const handleChange = useCallback( (event: React.ChangeEvent) => { @@ -47,12 +50,20 @@ export const Checkbox: React.FC = React.memo( inputProps={inputProps} /> - + {label && ( + + )} - {helperText} + + {error && !!errorText ? errorText : helperText} + ); } @@ -76,8 +87,15 @@ const Label = styled(InputLabel)` &.Mui-disabled { color: ${props => props.theme.palette.common.grey600}; } + + &.required::after { + content: "*"; + color: ${props => props.theme.palette.common.red}; + margin-inline-start: 4px; + } `; -const StyledFormHelperText = styled(FormHelperText)` - color: ${props => props.theme.palette.common.grey700}; +const StyledFormHelperText = styled(FormHelperText)<{ error?: boolean }>` + color: ${props => + props.error ? props.theme.palette.common.red700 : props.theme.palette.common.grey700}; `; diff --git a/src/webapp/components/form/FieldWidget.tsx b/src/webapp/components/form/FieldWidget.tsx new file mode 100644 index 00000000..2caf1113 --- /dev/null +++ b/src/webapp/components/form/FieldWidget.tsx @@ -0,0 +1,94 @@ +import React, { useCallback } from "react"; + +import { TextInput } from "../text-input/TextInput"; +import { UserSelector } from "../user-selector/UserSelector"; +import { MultipleSelector } from "../selector/MultipleSelector"; +import { Selector } from "../selector/Selector"; +import { RadioButtonsGroup } from "../radio-buttons-group/RadioButtonsGroup"; +import { TextArea } from "../text-input/TextArea"; +import { DatePicker } from "../date-picker/DatePicker"; +import { Checkbox } from "../checkbox/Checkbox"; +import { FormFieldState, updateFieldState } from "./FormState"; + +export type FieldWidgetProps = { + onChange: (updatedField: FormFieldState) => void; + field: FormFieldState; + disabled?: boolean; +}; + +export const FieldWidget: React.FC = React.memo((props): JSX.Element => { + const { field, onChange, disabled = false } = props; + + const handleChange = useCallback( + (newValue: FormFieldState["value"]) => { + onChange(updateFieldState(field, newValue)); + }, + [field, onChange] + ); + + const commonProps = { + id: field.id, + label: field.label, + onChange: handleChange, + helperText: field.helperText, + errorText: field.errors ? field.errors.join("\n") : "", + error: field.errors && field.errors.length > 0, + required: field.required && field.showIsRequired, + disabled: disabled, + }; + + switch (field.type) { + case "select": { + return field.multiple ? ( + + ) : ( + + ); + } + + case "radio": { + return ( + + ); + } + + case "text": + return field.multiline ? ( +