Skip to content

Commit

Permalink
Merge pull request #5 from EyeSeeTea/feature/create-event-form
Browse files Browse the repository at this point in the history
Add create disease outbreak event form in presentation layer
  • Loading branch information
bhavananarayanan authored Aug 9, 2024
2 parents 70b249c + d23914d commit 663e19a
Show file tree
Hide file tree
Showing 29 changed files with 2,124 additions and 109 deletions.
17 changes: 13 additions & 4 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -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-10T13:58:01.688Z\n"
"PO-Revision-Date: 2024-07-10T13:58:01.688Z\n"
"POT-Creation-Date: 2024-08-05T08:04:28.112Z\n"
"PO-Revision-Date: 2024-08-05T08:04:28.112Z\n"

msgid "Low"
msgstr ""
Expand Down Expand Up @@ -66,10 +66,10 @@ msgstr ""
msgid "Indicates required"
msgstr ""

msgid "Save"
msgid "Cancel"
msgstr ""

msgid "Cancel"
msgid "Save"
msgstr ""

msgid "Create Event"
Expand All @@ -90,6 +90,9 @@ msgstr ""
msgid "% Target Met"
msgstr ""

msgid "Status: "
msgstr ""

msgid "Dashboard"
msgstr ""

Expand All @@ -99,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 ""

Expand Down
15 changes: 12 additions & 3 deletions i18n/es.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2024-07-10T13:58:01.688Z\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"
Expand Down Expand Up @@ -65,10 +65,10 @@ msgstr ""
msgid "Indicates required"
msgstr ""

msgid "Save"
msgid "Cancel"
msgstr ""

msgid "Cancel"
msgid "Save"
msgstr ""

msgid "Create Event"
Expand All @@ -89,6 +89,9 @@ msgstr ""
msgid "% Target Met"
msgstr ""

msgid "Status: "
msgstr ""

msgid "Dashboard"
msgstr ""

Expand All @@ -98,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 ""

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { UserD2Repository } from "./data/repositories/UserD2Repository";
import { UserTestRepository } from "./data/repositories/UserTestRepository";
import { UserRepository } from "./domain/repositories/UserRepository";
import { GetCurrentUserUseCase } from "./domain/usecases/GetCurrentUserUseCase";
import { GetDiseaseOutbreakEventUseCase } from "./domain/usecases/GetDiseaseOutbreakEventUseCase";
import { D2Api } from "./types/d2-api";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;
Expand All @@ -15,6 +16,9 @@ function getCompositionRoot(repositories: Repositories) {
users: {
getCurrent: new GetCurrentUserUseCase(repositories.usersRepository),
},
diseaseOutbreakEvent: {
get: new GetDiseaseOutbreakEventUseCase(),
},
};
}

Expand Down
9 changes: 9 additions & 0 deletions src/domain/usecases/GetDiseaseOutbreakEventUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FutureData } from "../../data/api-futures";
import { DiseaseOutbreakEvent } from "../entities/DiseaseOutbreakEvent";
import { Future } from "../entities/generic/Future";

export class GetDiseaseOutbreakEventUseCase {
public execute(_id: string): FutureData<DiseaseOutbreakEvent> {
return Future.success({} as DiseaseOutbreakEvent);
}
}
17 changes: 14 additions & 3 deletions src/utils/tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -18,8 +23,14 @@ export function getReactComponent(children: ReactNode): RenderResult {
const context = getTestContext();

return render(
<AppContext.Provider value={context}>
<SnackbarProvider>{children}</SnackbarProvider>
</AppContext.Provider>
<MuiThemeProvider theme={muiTheme}>
<ThemeProvider theme={muiTheme}>
<OldMuiThemeProvider muiTheme={muiThemeLegacy}>
<AppContext.Provider value={context}>
<SnackbarProvider>{children}</SnackbarProvider>
</AppContext.Provider>
</OldMuiThemeProvider>
</ThemeProvider>
</MuiThemeProvider>
);
}
30 changes: 24 additions & 6 deletions src/webapp/components/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export const Checkbox: React.FC<CheckboxProps> = React.memo(
helperText = "",
disabled = false,
indeterminate = false,
errorText = "",
error = false,
required = false,
}) => {
const handleChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -47,12 +50,20 @@ export const Checkbox: React.FC<CheckboxProps> = React.memo(
inputProps={inputProps}
/>

<Label htmlFor={id} disabled={disabled}>
{label}
</Label>
{label && (
<Label
className={required ? "required" : ""}
htmlFor={id}
disabled={disabled}
>
{label}
</Label>
)}
</CheckboxWrapper>

<StyledFormHelperText id={`${id}-helper-text`}>{helperText}</StyledFormHelperText>
<StyledFormHelperText id={`${id}-helper-text`} error={error && !!errorText}>
{error && !!errorText ? errorText : helperText}
</StyledFormHelperText>
</Container>
);
}
Expand All @@ -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};
`;
94 changes: 94 additions & 0 deletions src/webapp/components/form/FieldWidget.tsx
Original file line number Diff line number Diff line change
@@ -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<FieldWidgetProps> = 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 ? (
<MultipleSelector
{...commonProps}
placeholder={field.placeholder}
selected={field.value}
options={field.options}
/>
) : (
<Selector
{...commonProps}
placeholder={field.placeholder}
selected={field.value}
options={field.options}
/>
);
}

case "radio": {
return (
<RadioButtonsGroup
{...commonProps}
selected={field.value}
options={field.options}
/>
);
}

case "text":
return field.multiline ? (
<TextArea {...commonProps} value={field.value} />
) : (
<TextInput {...commonProps} value={field.value} />
);

case "date":
return <DatePicker {...commonProps} value={field.value} />;

case "boolean": {
return <Checkbox {...commonProps} checked={field.value} />;
}

case "user": {
return (
<UserSelector
{...commonProps}
placeholder={field.placeholder}
selected={field.value}
options={field.options}
/>
);
}
}
});
53 changes: 53 additions & 0 deletions src/webapp/components/form/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";

import { useForm } from "./useForm";
import { FormState, FormFieldState } from "./FormState";
import { FormLayout } from "./FormLayout";
import { FormSection } from "./FormSection";
import { Layout } from "../layout/Layout";

export type FormProps = {
formState: FormState;
onFormChange: (newFormState: FormState, updatedField: FormFieldState) => void;
onSave: () => void;
onCancel?: () => void;
};

export const Form: React.FC<FormProps> = React.memo(props => {
const { formState, onFormChange, onSave, onCancel } = props;

const { formLocalState, handleUpdateFormField } = useForm(formState, onFormChange);

return (
<Layout title={formLocalState.title} subtitle={formLocalState.subtitle} hideSideBarOptions>
<FormLayout
title={formLocalState.titleDescripton}
subtitle={formLocalState.subtitleDescripton}
onSave={onSave}
onCancel={onCancel}
saveLabel={formLocalState.saveButtonLabel}
cancelLabel={formLocalState.cancelButtonLabel}
disableSave={!formLocalState.isValid}
>
{formLocalState.sections.map(section => {
if (!section.isVisible) return null;

return (
<FormSection
key={section.id}
id={section.id}
title={section.title}
hasSeparator
required={section.required}
direction={section.direction}
subsections={section.subsections}
fields={section.fields}
onUpdateField={handleUpdateFormField}
onClickInfo={section.onClickInfo}
/>
);
})}
</FormLayout>
</Layout>
);
});
Loading

0 comments on commit 663e19a

Please sign in to comment.