Skip to content

Commit

Permalink
feat: add complete event feature
Browse files Browse the repository at this point in the history
  • Loading branch information
9sneha-n committed Nov 3, 2024
1 parent ea6a5ed commit 7af1fae
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { GetConfigurationsUseCase } from "./domain/usecases/GetConfigurationsUse
import { ConfigurationsRepository } from "./domain/repositories/ConfigurationsRepository";
import { ConfigurationsD2Repository } from "./data/repositories/ConfigurationsD2Repository";
import { ConfigurationsTestRepository } from "./data/repositories/test/ConfigurationsTestRepository";
import { CompleteEventTrackerUseCase } from "./domain/usecases/CompleteEventTrackerUseCase";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;

Expand Down Expand Up @@ -106,6 +107,7 @@ function getCompositionRoot(repositories: Repositories) {
repositories.configurationsRepository,
repositories.teamMemberRepository
),
complete: new CompleteEventTrackerUseCase(repositories),
},
incidentActionPlan: {
get: new GetIncidentActionByIdUseCase(repositories),
Expand Down
43 changes: 40 additions & 3 deletions src/data/repositories/DiseaseOutbreakEventD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { D2Api } from "../../types/d2-api";
import { DiseaseOutbreakEventRepository } from "../../domain/repositories/DiseaseOutbreakEventRepository";
import { apiToFuture, FutureData } from "../api-futures";
import { DiseaseOutbreakEventBaseAttrs } from "../../domain/entities/disease-outbreak-event/DiseaseOutbreakEvent";
import { Id, ConfigLabel } from "../../domain/entities/Ref";
import { Id } from "../../domain/entities/Ref";
import {
mapDiseaseOutbreakEventToTrackedEntityAttributes,
mapTrackedEntityAttributesToDiseaseOutbreak,
Expand All @@ -13,6 +13,7 @@ import { getProgramTEAsMetadata } from "./utils/MetadataHelper";
import { assertOrError } from "./utils/AssertOrError";
import { Future } from "../../domain/entities/generic/Future";
import { getAllTrackedEntitiesAsync } from "./utils/getAllTrackedEntities";
import { D2TrackerEnrollment } from "@eyeseetea/d2-api/api/trackerEnrollments";

export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRepository {
constructor(private api: D2Api) {}
Expand Down Expand Up @@ -84,8 +85,44 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep
);
}

getConfigStrings(): FutureData<ConfigLabel[]> {
throw new Error("Method not implemented.");
complete(id: Id): FutureData<void> {
return apiToFuture(
this.api.tracker.enrollments.get({
fields: {
enrollment: true,
enrolledAt: true,
occurredAt: true,
},
trackedEntity: id,
enrolledBefore: new Date().toISOString(),
program: RTSL_ZEBRA_PROGRAM_ID,
orgUnit: RTSL_ZEBRA_ORG_UNIT_ID,
})
).flatMap(enrollmentResponse => {
const currentEnrollment = enrollmentResponse.instances[0];
const currentEnrollmentId = currentEnrollment?.enrollment;
if (!currentEnrollment || !currentEnrollmentId) {
return Future.error(new Error(`Enrollment not found for Event Tracker`));
}

const enrollment: D2TrackerEnrollment = {
...currentEnrollment,
orgUnit: RTSL_ZEBRA_ORG_UNIT_ID,
program: RTSL_ZEBRA_PROGRAM_ID,
trackedEntity: id,
status: "COMPLETED",
};

return apiToFuture(
this.api.tracker.post({ importStrategy: "UPDATE" }, { enrollments: [enrollment] })
).flatMap(response => {
if (response.status !== "OK") {
return Future.error(
new Error(`Error completing disease outbreak event : ${response.message}`)
);
} else return Future.success(undefined);
});
});
}

//TO DO : Implement delete/archive after requirement confirmation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { DiseaseOutbreakEventRepository } from "../../../domain/repositories/Dis
import { FutureData } from "../../api-futures";

export class DiseaseOutbreakEventTestRepository implements DiseaseOutbreakEventRepository {
complete(id: Id): FutureData<void> {
return Future.success(undefined);
}
get(id: Id): FutureData<DiseaseOutbreakEventBaseAttrs> {
return Future.success({
id: id,
Expand Down
4 changes: 2 additions & 2 deletions src/domain/repositories/DiseaseOutbreakEventRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { FutureData } from "../../data/api-futures";
import { DiseaseOutbreakEventBaseAttrs } from "../entities/disease-outbreak-event/DiseaseOutbreakEvent";
import { ConfigLabel, Id } from "../entities/Ref";
import { Id } from "../entities/Ref";

export interface DiseaseOutbreakEventRepository {
get(id: Id): FutureData<DiseaseOutbreakEventBaseAttrs>;
getAll(): FutureData<DiseaseOutbreakEventBaseAttrs[]>;
save(diseaseOutbreak: DiseaseOutbreakEventBaseAttrs): FutureData<Id>;
getConfigStrings(): FutureData<ConfigLabel[]>;
complete(id: Id): FutureData<void>;
}
15 changes: 15 additions & 0 deletions src/domain/usecases/CompleteEventTrackerUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FutureData } from "../../data/api-futures";
import { Id } from "../entities/Ref";
import { DiseaseOutbreakEventRepository } from "../repositories/DiseaseOutbreakEventRepository";

export class CompleteEventTrackerUseCase {
constructor(
private options: {
diseaseOutbreakEventRepository: DiseaseOutbreakEventRepository;
}
) {}

public execute(id: Id): FutureData<void> {
return this.options.diseaseOutbreakEventRepository.complete(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { Box, Button, Typography } from "@material-ui/core";
import { UserCard } from "../../user-selector/UserCard";
import { RouteName, useRoutes } from "../../../hooks/useRoutes";
import { EditOutlined } from "@material-ui/icons";
import { CheckOutlined } from "@material-ui/icons";
import { Loader } from "../../loader/Loader";
import { useSnackbar } from "@eyeseetea/d2-ui-components";
import { FormSummaryData } from "../../../pages/event-tracker/useDiseaseOutbreakEvent";
import { Maybe } from "../../../../utils/ts-utils";
import { FormType } from "../../../pages/form-page/FormPage";
import { Id } from "../../../../domain/entities/Ref";
import { useAppContext } from "../../../contexts/app-context";

export type FormSummaryProps = {
export type EventTrackerFormSummaryProps = {
id: Id;
formType: FormType;
formSummary: Maybe<FormSummaryData>;
Expand All @@ -22,7 +24,8 @@ export type FormSummaryProps = {

const ROW_COUNT = 3;

export const FormSummary: React.FC<FormSummaryProps> = React.memo(props => {
export const EventTrackerFormSummary: React.FC<EventTrackerFormSummaryProps> = React.memo(props => {
const { compositionRoot } = useAppContext();
const { id, formType, formSummary, summaryError } = props;
const { goTo } = useRoutes();
const snackbar = useSnackbar();
Expand All @@ -38,6 +41,18 @@ export const FormSummary: React.FC<FormSummaryProps> = React.memo(props => {
goTo(RouteName.EDIT_FORM, { formType: formType, id: id });
}, [formType, goTo, id]);

const onCompleteClick = useCallback(() => {
compositionRoot.diseaseOutbreakEvent.complete.execute(id).run(
() => {
snackbar.success(i18n.t("Event completed"));
},
err => {
snackbar.error(i18n.t(`Failed to complete event: ${err.message}`));
console.error(err);
}
);
}, []);

const editButton = (
<Button
variant="outlined"
Expand All @@ -49,6 +64,17 @@ export const FormSummary: React.FC<FormSummaryProps> = React.memo(props => {
</Button>
);

const completeButton = (
<Button
variant="outlined"
color="secondary"
onClick={onCompleteClick}
startIcon={<CheckOutlined />}
>
{i18n.t("Complete Event")}
</Button>
);

const getSummaryColumn = useCallback((index: number, label: string, value: string) => {
return (
<Typography key={index}>
Expand All @@ -66,6 +92,7 @@ export const FormSummary: React.FC<FormSummaryProps> = React.memo(props => {
title={formSummary.subTitle}
hasSeparator={true}
headerButton={editButton}
secondaryHeaderButton={completeButton}
titleVariant="secondary"
>
<SummaryContainer>
Expand Down
12 changes: 10 additions & 2 deletions src/webapp/components/section/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type SectionProps = {
lastUpdated?: string;
children: React.ReactNode;
headerButton?: React.ReactNode;
secondaryHeaderButton?: React.ReactNode;
hasSeparator?: boolean;
titleVariant?: "primary" | "secondary";
};
Expand All @@ -18,6 +19,7 @@ export const Section: React.FC<SectionProps> = React.memo(
title = "",
lastUpdated = "",
headerButton,
secondaryHeaderButton,
hasSeparator = false,
children,
titleVariant = "primary",
Expand All @@ -40,15 +42,21 @@ export const Section: React.FC<SectionProps> = React.memo(
) : null}
</TitleContainer>

{headerButton ? <div>{headerButton}</div> : null}
<ButtonContainer>
{headerButton ? <div>{headerButton}</div> : null}
{secondaryHeaderButton ? <div>{secondaryHeaderButton}</div> : null}
</ButtonContainer>
</Header>

<Content>{children}</Content>
</SectionContainer>
);
}
);

const ButtonContainer = styled.div`
display: flex;
gap: 5px;
`;
const SectionContainer = styled.section<{ $hasSeparator?: boolean }>`
width: 100%;
margin-block-end: ${props => (props.$hasSeparator ? "0" : "24px")};
Expand Down
4 changes: 2 additions & 2 deletions src/webapp/pages/event-tracker/EventTrackerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useParams } from "react-router-dom";
import { AddCircleOutline, EditOutlined } from "@material-ui/icons";
import i18n from "../../../utils/i18n";
import { Layout } from "../../components/layout/Layout";
import { FormSummary } from "../../components/form/form-summary/FormSummary";
import { EventTrackerFormSummary } from "../../components/form/form-summary/EventTrackerFormSummary";
import { Chart } from "../../components/chart/Chart";
import { Section } from "../../components/section/Section";
import { BasicTable, TableColumn } from "../../components/table/BasicTable";
Expand Down Expand Up @@ -69,7 +69,7 @@ export const EventTrackerPage: React.FC = React.memo(() => {

return (
<Layout title={i18n.t("Event Tracker")} lastAnalyticsRuntime={lastAnalyticsRuntime}>
<FormSummary
<EventTrackerFormSummary
id={id}
formType="disease-outbreak-event"
formSummary={formSummary}
Expand Down

0 comments on commit 7af1fae

Please sign in to comment.