-
Notifications
You must be signed in to change notification settings - Fork 0
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
Unique Beneficiaries #551
Unique Beneficiaries #551
Conversation
Task linked: CU-8695qbyrz Unique Beneficiaries |
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.
Thanks @eperedo , some things I've noticed while I wait for the technical validation:
'Manage Unique Beneficiaries Periods'
-
You can create two periods with different labels but exactly the same Start and End month. We should not allow to create two periods with exactly same Start and End month (that includes the default ones, so we should not allow either a custom period with exactly the same months as the Semi-annual or Annual one)
-
You can configure a period where End month < Start month. We should not allow it as it doesn't make any sense. In fact, if you have such period configured, you cannot access the 'Project Indicators Validation' as it prompts a Conflict error.
-
Is there a reason why we identify the months with numbers instead of the actual name? I'd rather have the names, unless this is an specific request from the client I am not aware of.
'Project Indicators Validation'
-
If you try to enter a Negative value in any field of the 'Project Indicators Validation', app crashes. I understand this is not a valid use case, but we should show a pop-up preventing such config instead of the app crashing.
-
If you change a value in the Editable New and click on Save, it doesn't get saved. Only the Returning field and the Comments field changes get saved.
-
The identification and validation of updated data only seems to work for the custom periods if you have clicked on Save at least once for such period. It should work right for the beginning, no need to click on Save at least once (actually the default periods behave this way)
'Unique Beneficiaries' (the menu on top)
-
The period dropdown should behave slightly different. We should get all the periods configured for all the projects, but instead of showing the period labels, show a unique list of Start and End month for all the periods available (e.g., if Project A has a period called 'Q1' from Jan-Mar and Project B has a period called 'Quarter1' from Jan-Mar, we should show in the dropdown the text 'January - March (Q1, Quarter1)', so it is understood that such period is configured with two different labels.
-
If you have a Period selected and move to a different Country where that Period is not configured, a message says that 'No projects found for selected Period', which is true but it does not make sense to show the message it as the Period itself is not selected nor shown in the dropdown. We should just clear the Period dropdown if the selected one doesn't exist for the new country selected and not show any message.
-
If you select one of the Default Periods, only one project is loaded. This does not happen when selecting a Custom Period, for which all projects are loaded.
-
Right now for each project it is shown <project_name> and below <project_start_date> - <project_end_date>. We should add one extra line below to show the <period_label> that is matching for each project the period selected in the dropdown
-
Save button behavior is odd. If you change different periods, all changes are 'saved' for that session, but only the periods where you actually clicked on 'Saved' are saved from one session to the other (by session I mean getting back to the main screen and inside 'Unique Beneficiaries' again. I think by default we should save every change in the report without the need of a Save button.
-
Excel sheet being downloaded only shows the indicator ID, but not the name. It should be name (ID), just like it is shown in the app
-
When downloading data for a project that doesn't have such period configured (and shows N/A in the web), the spreadsheet shows 0 and 'No' instead of the N/A label
-
The spreadsheet is missing a row at the end with the sum of all beneficiaries from all projects downloaded with the label 'Country Unique Beneficiaries'
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.
Looking very good, cool to start having some Clean Arch patterns in the project :+1
Some comments inline, mostly very minor:
msgstr "" | ||
|
||
msgid "January" | ||
msgstr "" |
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'd want to use the translations from some library, but as the project only has EN, it's not really a problem. It would be different for CPR, for example, with dozens of languages to translate.
} = props; | ||
const snackbar = useSnackbar(); | ||
const [filter, setFilter] = useState<Filter>({}); | ||
const [filter, setFilter] = useState<Filter>(initialFilters); |
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.
cool, now we can remove <Filter>
|
||
const UniqueIndicatorsStep: React.FC<StepProps> = props => { | ||
const { project } = props; | ||
const getSelection: DataElementsStepProps["onSelect"] = React.useCallback( |
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 put the type to the type argument of useCallback, but maybe you prefer this style for some reason?
{...props} | ||
onSelect={getSelection} | ||
dataElementsSet={project.uniqueIndicators} | ||
initialFilters={{ peopleOrBenefit: "people" }} |
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.
It can be moved to root const so we keep the prop immutable
} | ||
|
||
function showAction(rows: UniqueBeneficiariesPeriodsAttrs[]): boolean { | ||
return rows.filter(row => !UniqueBeneficiariesPeriod.isProtected(row)).length === 1; |
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 the Attrs/Props pair of a Struct class is used solely to create it. Then you work with the instance. A more idiomatic approach: UniqueBeneficiariesPeriod.create(row).isProtected()
.
} from "../../domain/entities/UniqueBeneficiariesPeriod"; | ||
import i18n from "../../locales"; | ||
|
||
export type ActionTable = { action: string; id: 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.
Is it possible to narrow down the type of action?
private async getProjectsByIds(projectIds: Id[]): Promise<ProjectCountry[]> { | ||
const projects = await promiseMap(projectIds, id => Project.get(this.api, this.config, id)); | ||
return projects.map(project => ({ | ||
closedDate: project.endDate?.toISOString() || "", |
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.
move after openingDate?
static calculateTotalValue(editable: Maybe<number>, returning: Maybe<number>): number { | ||
if (!editable) return returning || 0; | ||
if (!returning) return editable; | ||
return editable + returning; |
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.
Probably more readable with if / else if / else
|
||
const errors: ValidationError<IndicatorValidation>[] = [periodProperty].filter( | ||
validation => validation.errors.length > 0 | ||
); |
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 won't specify the type when the source is already typed: return [periodProperty].filter(validation => validation.errors.length > 0)
if (options.action === "add" || options.action === "edit") { | ||
setSavePeriodModal(true); | ||
} else if (options.action === "delete") { | ||
setDeleteModal(true); |
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.
maybe with a switch?
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:
const getSelection: DataElementsStepProps["onSelect"] = React.useCallback( | ||
(sectorId, dataElementIds) => { | ||
const getSelection = React.useCallback( | ||
(sectorId: Id, dataElementIds: Id[]) => { |
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 should've been more specific, I mean that pattern: React.useCallback<DataElementsStepProps["onSelect"]>
, and now the arguments of the function are automatically typed.
Again, not saying this is the best style, but for consistency. The final goal is too get the most descriptive error at the most relevant point if the types do not match.
return editable; | ||
} else { | ||
return editable + returning; | ||
} |
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.
Now that I see it like this, isn't equivalent to: return (editable ?? 0) + (returning ?? 0)
?
item => | ||
item.period.startDateMonth === period.startDateMonth && | ||
item.period.endDateMonth === period.endDateMonth | ||
) |
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.
There are lots of comparisons for matching start/end. In these cases. the first thought is, can I move somehow the logic to entities? does this make sense? item.period.equalDates(period)
(or whatever name)
} | ||
|
||
return this.saveSettings(settings, periodExist, 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.
[subjective] when we are writing the final logic of a method (not an early return), it's very declarative to use a full "if/else if/else" block.
.compact() | ||
.uniqBy(getId) | ||
.value(); | ||
function getAllPeriods(settings: IndicatorReport[]): UniqueBeneficiariesPeriod[] { |
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.
settings: IndicatorReport[]
variable name does not match type.
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.
Cool, all code looks good to me!
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.
Thanks @eperedo . A couple of things we still need to fix:
-
In the 'Unique Beneficiaries' menu, in the dropdown we are doing it right, <Start_Month> - <End_Month> and then a list of all the custom period labels that match such period within brackets. But in the project list, we are not doing it right. Each project should show - <End_Month> and ONLY in brackets its period label, so the user gets an understanding on which is the period label that is matching such criteria.
-
The pop-up for changed values has an 'odd' behavior. To be reviewed in a call as it is difficult to understand the rationale for current logic.
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 now @eperedo , thanks!!!
📌 References
Issue: Closes https://app.clickup.com/t/8695qbyrz
d2-api: Requires allow dataElement param in dataValueSets endpoint d2-api#152
📝 Implementation
I'm using the branch feature/clone-projects as the base branch to facilitate code review
🔥 Notes for the reviewer
🎨 Screenshots
New options for periods, indicators validation and unique beneficiaries report
Unique Periods administration
Unique indicators selection
Indicators Validation
Unique Beneficiaries Report
#8695qbyrz