-
Notifications
You must be signed in to change notification settings - Fork 1
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
add indicators screen #471
Conversation
…onfiguration into feature/add-indicators
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In-line comments:
src/data/repositories/D2ApiConfig.ts
Outdated
d2Response.dataElementGroupSets, | ||
metadataCodes.dataElementGroupSets.status | ||
), | ||
}, |
There was a problem hiding this comment.
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)
src/data/repositories/D2ApiConfig.ts
Outdated
@@ -6,11 +6,27 @@ import { apiToFuture } from "$/data/api-futures"; | |||
|
|||
export const metadataCodes = { |
There was a problem hiding this comment.
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 => { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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())) |
There was a problem hiding this comment.
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
There was a problem hiding this 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>[] { |
There was a problem hiding this comment.
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.
src/domain/entities/Indicator.ts
Outdated
@@ -12,7 +12,7 @@ export type IndicatorAttrs = { | |||
theme: string; | |||
status: string; | |||
type: "outputs" | "outcomes"; | |||
scope: "core" | "local" | "donor"; | |||
scope: ScopeType; |
There was a problem hiding this comment.
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; | |||
|
There was a problem hiding this comment.
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[]; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)
There was a problem hiding this 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
📌 References
📝 Implementation
dataElements
within dataSet and sectionsindicators
within dataSet and sections📹 Screenshots/Screen capture
Project-Configuration-app_indicators.webm
🔥 Notes to the tester
Using PR 470 as base branch to facilitate code review
#8696ewg1c