Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add indicators screen #471

Merged
merged 4 commits into from
Dec 17, 2024

Conversation

eperedo
Copy link
Contributor

@eperedo eperedo commented Dec 2, 2024

📌 References

📝 Implementation

  • outputs indicators are saved as dataElements within dataSet and sections
  • outcomes indicators are saved as indicators within dataSet and sections
  • outcomes indicators can include extra dataElements when the indicator (DHIS) include references to dataElements in: numerator, denominator or code.

📹 Screenshots/Screen capture

Project-Configuration-app_indicators.webm

🔥 Notes to the tester

Using PR 470 as base branch to facilitate code review

#8696ewg1c

@eperedo eperedo mentioned this pull request Dec 6, 2024
2 tasks
Base automatically changed from feature/setup-screen to development-project-configurator December 9, 2024 08:53
Copy link
Collaborator

@tokland tokland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In-line comments:

d2Response.dataElementGroupSets,
metadataCodes.dataElementGroupSets.status
),
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing one-liner is not generally a goal, but for this kind of code, the structure will look much nicer if are able to have one-liners. For that, we create function helpers that abstract the repetitions and local variables (shortening the name, only if necessary). For example, the previous three calls can be captured with something like that:

            const { dataElementGroupSets } = metadataCodes;
            const getGroupSet = (groupSetCode: string) =>
                getOrThrow(d2Response.dataElementGroupSets, groupSetCode);

And now is used like this:

            coreCompetency: getGroupSet(dataElementGroupSets.coreCompetency),

Same for the rest of gets (name of the helper is just a proposal)

@@ -6,11 +6,27 @@ import { apiToFuture } from "$/data/api-futures";

export const metadataCodes = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename looks a bit confusing to me, as by D2Api we think of the library. Maybe D2Metadata?

@@ -101,7 +103,7 @@ export class DataSetD2Repository implements DataSetRepository {
const ids = dataSets.map(dataSet => dataSet.id);

return this.d2DataSetApi.getConfig().flatMap(config => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

save method too long, split steps

import { FutureData } from "$/domain/entities/generic/Future";
import { CoreCompetencyRepository } from "$/domain/repositories/CoreCompetencyRepository";

export class GetCoreCompetencyUseCase {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that at least this use case has no usage in the app. Typically we won't do that, only create it when known to be used, but of course, you know more context and you may be sure it will be used isolatedly in the UI.

dataSet.name.toLowerCase() === options.name.toLowerCase()
);
});
return this.dataSetUtils.dataSetNameExists(options);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dataSetNameExists typically we want a verb in a method.

onClose: () => void;
onFilterChange: (scope: string, type: FilterType) => void;
onFilterChange: (scope: ChipItem | ChipItem[], type: FilterType) => void;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typically, we try to avoid this JS (T | T[]) acceptance (we accept T[] and create another function that wrap the single case)

const isArray = Array.isArray(value);
switch (filterType) {
case "scope":
if (!isArray) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and then we can avoid this ugly code :)

return DataSet.initial(getUid(new Date().getTime().toString()));
});
const [dataSet, updateDataSet] = React.useState<DataSet>(
DataSet.initial(getUid(new Date().getTime().toString()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

abstract to get RandomUid or similar

@eperedo eperedo requested a review from tokland December 10, 2024 15:35
Copy link
Collaborator

@tokland tokland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some in-line comments:

validate(): Either<ValidationError<DataSet>[], DataSet> {
const allErrors = this.getValidationErrors();
return allErrors.length === 0 ? Either.success(this) : Either.error(allErrors);
validate(): ValidationError<DataSet>[] {
Copy link
Collaborator

@tokland tokland Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor, no need to change, just a general note: Using ValidationError<DataSet>[] constrains the return type to always be an array. For a more flexible approach, consider ValidationErrors<DataSet> instead, allowing it to represent any structure. This way, the type can be reshaped in the future without requiring changes to all signatures where it is passed, focusing only on the code that interacts with its internals.

@@ -12,7 +12,7 @@ export type IndicatorAttrs = {
theme: string;
status: string;
type: "outputs" | "outcomes";
scope: "core" | "local" | "donor";
scope: ScopeType;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep the naming consistent: Scope or IndicatorScope

src/utils/uid.ts Outdated
@@ -45,6 +45,9 @@ export function generateUid(): string {
return randomChars;
}

// @ts-ignore
window.generateUid = generateUid;

Copy link
Collaborator

@tokland tokland Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

@@ -135,7 +138,7 @@ export const FilterIndicators = React.memo((props: FilterIndicatorsProps) => {
export type ChipFilterProps = {
items: ChipItem[];
label: string;
onChange: (item: ChipItem | ChipItem[]) => void;
onChange: (item: ChipItem[]) => void;
value: string | string[];
Copy link
Collaborator

@tokland tokland Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I hadn't seen that. Can we follow the same idea for value, to be only string[]?

The general idea is that, when using TS code in our control, we never have to call functions like isArray, isObject, isString, etc.

(When we really need to have this kind of distinctions, in other scenarios, we use type-safe discriminating union types)

coreCompetency: getOrThrow(
d2Response.dataElementGroupSets,
coreCompetency: getOrThrowMetadata(
"dataElementGroupSets",
metadataCodes.dataElementGroupSets.coreCompetency
Copy link
Collaborator

@tokland tokland Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were not able to get single lines, but yes, it may hinder readability if we push it too much. let's keep as is.

(other strategies: move to separate function to reduce indentation level, create shorter name for helper functions, rename to shorter names the variables in the block)

@eperedo eperedo requested a review from tokland December 16, 2024 17:28
Copy link
Collaborator

@tokland tokland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good by me, code-wise

@MiquelAdell MiquelAdell merged commit 6fe29f3 into development-project-configurator Dec 17, 2024
1 check passed
@MiquelAdell MiquelAdell deleted the feature/add-indicators branch December 17, 2024 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants