Skip to content

Commit

Permalink
Disable button when invalid form and submitting, Refactor mission dat…
Browse files Browse the repository at this point in the history
…a download (#1313)

* Disable button when invalid form and submitting

* Refactor mission data download

* Remove surplus test suites

* Update snapshots probably regressed in previous i18n changes
  • Loading branch information
peterMuriuki authored Dec 11, 2023
1 parent cf83a0c commit 3d37537
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

exports[`post confirmation success card works correctly: card title 1`] = `"“processed.csv” inventory items successfully added"`;

exports[`post confirmation success card works correctly: full snapshot 1`] = `"“processed.csv” inventory items successfully added5 inventory items added to Service point inventory.  Inventory may take a few minutes to appear.Upload another file"`;
exports[`post confirmation success card works correctly: full snapshot 1`] = `"“processed.csv” inventory items successfully added5 inventory items added to Service point inventory.  Inventory may take a few minutes to appear.Upload another file"`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`pre confirmation error card renders correctly 1`] = `[Function]`;

exports[`pre confirmation error card renders correctly: full general snapshot 1`] = `"Use a CSV file to add service point inventoryplease fix the errors listed below, then retry csv uploadRow numberErrors2Service point ID does not exist3Service point ID does not exist, UNICEF section is not valid, Donor is not valid15 / pageRetry"`;
exports[`pre confirmation error card renders correctly: full general snapshot 1`] = `"Use a CSV file to add service point inventoryplease fix the errors listed below, then retry csv uploadRow numberErrors2Service point ID does not exist3Service point ID does not exist, UNICEF section is not valid, Donor is not valid15 / pageRetry"`;

exports[`pre confirmation error card renders correctly: table row 1`] = `"Row numberErrors"`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

exports[`Inventory bulk upload renders correctly: start upload page 1`] = `"Add inventory via CSVUse a CSV file to add service point inventoryYou’ll get a chance to review before committing inventory updates.Select CSV file"`;

exports[`Inventory bulk upload shows post confirmation error card: post confirmation error page 1`] = `"Add inventory via CSVProcessing error: inventory items failed to be added0 inventory items failed to be added from “”. To add items, follow these steps: Extract the rows listed below from \\"\\"Paste the rows into a new CSV fileUpload the CSV fileInventory items not listed below were successfully added to theService point inventory. Caution: do not re-upload the successful items or duplicates will be created.Inventory items from “” that were not addedRow numberErrorsNo data"`;
exports[`Inventory bulk upload shows post confirmation error card: post confirmation error page 1`] = `"Add inventory via CSVProcessing error: inventory items failed to be added0 inventory items failed to be added from “”. To add items, follow these steps: Extract the rows listed below from \\"\\"Paste the rows into a new CSV fileUpload the CSV fileInventory items not listed below were successfully added to theService point inventory. Caution: do not re-upload the successful items or duplicates will be created.Inventory items from “” that were not addedRow numberErrorsNo data"`;

exports[`Inventory bulk upload shows post confirmation success card: post confirmation success page 1`] = `"Add inventory via CSV“” inventory items successfully added0 inventory items added to Service point inventory.  Inventory may take a few minutes to appear.Upload another file"`;
exports[`Inventory bulk upload shows post confirmation success card: post confirmation success page 1`] = `"Add inventory via CSV“” inventory items successfully added0 inventory items added to Service point inventory.  Inventory may take a few minutes to appear.Upload another file"`;

exports[`Inventory bulk upload shows post confirmation upload card: post confirmation upload page 1`] = `"Add inventory via CSVInventory is being added to service points…Inventory may take a few minutes to appear."`;

exports[`Inventory bulk upload shows pre confirmation error page: pre confirmation error page 1`] = `"Add inventory via CSVUse a CSV file to add service point inventoryplease fix the errors listed below, then retry csv uploadRow numberErrorsNo dataRetry"`;
exports[`Inventory bulk upload shows pre confirmation error page: pre confirmation error page 1`] = `"Add inventory via CSVUse a CSV file to add service point inventoryplease fix the errors listed below, then retry csv uploadRow numberErrorsNo dataRetry"`;

exports[`Inventory bulk upload shows pre confirmation success page: pre confirmation success page 1`] = `"Add inventory via CSV“” ready0 inventory items will be added to service points. Do you wish to proceed?Proceed with adding inventoryCancel"`;

Expand Down
43 changes: 34 additions & 9 deletions packages/location-management/src/components/LocationForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Form, Input, Space, Button, Radio } from 'antd';
import { Form, Input, Space, Button, Radio, FormInstance } from 'antd';
import { sendErrorNotification, sendSuccessNotification } from '@opensrp/notifications';
import { Redirect } from 'react-router';
import { ExtraFields } from './ExtraFields';
Expand Down Expand Up @@ -175,6 +175,7 @@ const LocationForm = (props: LocationFormProps) => {
is_jurisdiction: values.isJurisdiction,
};

setSubmitting(true);
postPutLocationUnit(payload, opensrpBaseURL, isEditMode, params)
.then(() => {
afterSubmit(payload);
Expand Down Expand Up @@ -353,14 +354,7 @@ const LocationForm = (props: LocationFormProps) => {

<FormItem {...tailLayout}>
<Space>
<Button
type="primary"
id="location-form-submit-button"
disabled={isSubmitting}
htmlType="submit"
>
{isSubmitting ? t('Saving') : t('Save')}
</Button>
<SubmitButton form={form} isSubmitting={isSubmitting} />
<Button id="location-form-cancel-button" onClick={() => onCancel()}>
{t('Cancel')}
</Button>
Expand All @@ -375,3 +369,34 @@ const LocationForm = (props: LocationFormProps) => {
LocationForm.defaultProps = defaultProps;

export { LocationForm };

const SubmitButton = ({ form, isSubmitting }: { form: FormInstance; isSubmitting: boolean }) => {
const { t } = useTranslation();
const [submittable, setSubmittable] = React.useState(false);

// Watch all values
const values = Form.useWatch([], form);

React.useEffect(() => {
form.validateFields({ validateOnly: true }).then(
() => {
setSubmittable(true);
},
() => {
setSubmittable(false);
}
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values]);

return (
<Button
type="primary"
id="location-form-submit-button"
disabled={isSubmitting || !submittable}
htmlType="submit"
>
{isSubmitting ? t('Saving') : t('Save')}
</Button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ exports[`LocationForm renders correctly: status label 1`] = `
exports[`LocationForm renders correctly: submit button 1`] = `
<button
className="ant-btn css-dev-only-do-not-override-k7429z ant-btn-primary"
disabled={false}
disabled={true}
id="location-form-submit-button"
onClick={[Function]}
type="submit"
Expand Down
35 changes: 31 additions & 4 deletions packages/opensrp-plans/src/components/MissionData/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { useTranslation } from '../../mls';
import { loadTasksIndicators, TaskCount, TaskParams } from '../../helpers/dataLoaders';
import { CommonProps, defaultCommonProps } from '@opensrp/plan-form';
import { useHandleBrokenPage } from '@opensrp/react-utils';
import { BuildDownloadUrl } from '../../helpers/utils';
import {
OPENSRP_BUSINESS_STATUS_HAS_PROBLEM,
OPENSRP_TASK_STATUS_COMPLETED,
} from '../../constants';
import { sendErrorNotification } from '@opensrp/notifications';
import { downloadMissionData } from '../../helpers/utils';

const { Title, Text } = Typography;

Expand Down Expand Up @@ -98,9 +99,7 @@ const MissionData = (props: MissionDataProps) => {
<Text>{t('Number of flagged products')}</Text>:&nbsp;
<Text type="secondary">{flaggedProducts}</Text>
</p>
<a href={BuildDownloadUrl(baseURL, plan.identifier)} download>
<Button type="primary">{t('Download mission data')}</Button>
</a>
<DownloadMissionData baseUrl={baseURL} plan={plan} />
</Space>
</Card>
) : null;
Expand All @@ -109,3 +108,31 @@ const MissionData = (props: MissionDataProps) => {
MissionData.defaultProps = defaultProps;

export { MissionData };

// Download Mission button.

interface DownloadMissionDataProps {
baseUrl: string;
plan: PlanDefinition;
}

export const DownloadMissionData = (props: DownloadMissionDataProps) => {
const { baseUrl, plan } = props;
const { t } = useTranslation();
const [downloading, setDownloading] = useState(false);
const handleClick = () => {
setDownloading(true);
downloadMissionData(baseUrl, plan)
.catch(() => {
sendErrorNotification(t('Mission data download failed.'));
})
.finally(() => {
setDownloading(false);
});
};
return (
<Button type="primary" disabled={downloading} onClick={handleClick}>
{t('Download mission data')}
</Button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ describe('mission data listing & download', () => {
`"Mission dataService points visited: 13Products checked: 7Number of flagged products: 3Download mission data"`
);
expect(wrapper.find('Button').text()).toEqual('Download mission data');
// downloads mission data
expect(wrapper.find('a').at(0).props().href).toEqual(
'https://opensrp-stage.smartregister.org/opensrp/rest/event/export-data?eventTypes=flag_problem,service_point_check,looks_good,record_gps&planIdentifier=335ef7a3-7f35-58aa-8263-4419464946d8'
);

expect(fetch.mock.calls).toEqual([
[
'https://opensrp-stage.smartregister.org/opensrp/rest/task/search?planIdentifier=335ef7a3-7f35-58aa-8263-4419464946d8&code=service_point_check&returnTaskCountOnly=true&status=Completed',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Update plan Page planform is configured correctly: full text snapshot 1`] = `"Edit missionIntervention TypeSupply ManagementPlan TitleStatusactivecompleteretiredActive date rangeDescription0 / 200Add JurisdictionActivitiesProduct CheckActionDescriptionCheck for all products (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsFix Product ProblemActionDescriptionFix problems for all products (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsRecord GPSActionDescriptionRecord GPS for all service points (100%) without GPS within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsService Point CheckActionDescriptionConduct checkfor all service points (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsSave PlanCancel"`;

exports[`Update plan Page renders correctly with store(for editing plans): full text snapshot 1`] = `"Edit missionIntervention TypeSupply ManagementPlan TitleStatusactivecompleteretiredActive date rangeDescription0 / 200Add JurisdictionActivitiesProduct CheckActionDescriptionCheck for all products (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsFix Product ProblemActionDescriptionFix problems for all products (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsRecord GPSActionDescriptionRecord GPS for all service points (100%) without GPS within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsService Point CheckActionDescriptionConduct checkfor all service points (100%) within the jurisdictionReasonRoutineGoalQuantityunknownStart DateEnd DatePriorityMedium PriorityTriggers and ConditionsSave PlanCancel"`;
13 changes: 1 addition & 12 deletions packages/opensrp-plans/src/helpers/tests/utils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@ import {
ACTIVE_PLANS_LIST_VIEW_URL,
COMPLETE_PLANS_LIST_VIEW_URL,
DRAFT_PLANS_LIST_VIEW_URL,
OPENSRP_API_BASE_URL,
SORT_BY_EFFECTIVE_PERIOD_START_FIELD,
RETIRED_PLANS_LIST_VIEW_URL,
} from '../../constants';
import * as planDefinitionFixtures from '../../ducks/planDefinitions/tests/fixtures';
import { InterventionType, PlanDefinition, PlanStatus } from '@opensrp/plan-form-core';
import {
descendingOrderSort,
isPlanDefinitionOfType,
getPlanType,
BuildDownloadUrl,
} from '../utils';
import { descendingOrderSort, isPlanDefinitionOfType, getPlanType } from '../utils';
import { redirectPathGetter } from '../common';

describe('helpers/utils', () => {
Expand Down Expand Up @@ -76,9 +70,4 @@ describe('helpers/utils', () => {
expect(redirectPathGetter(PlanStatus.COMPLETE)).toEqual(COMPLETE_PLANS_LIST_VIEW_URL);
expect(redirectPathGetter(PlanStatus.RETIRED)).toEqual(RETIRED_PLANS_LIST_VIEW_URL);
});
it('tests BuildDownloadUrl', () => {
expect(BuildDownloadUrl(OPENSRP_API_BASE_URL, '123')).toEqual(
'https://opensrp-stage.smartregister.org/opensrp/rest/event/export-data?eventTypes=flag_problem,service_point_check,looks_good,record_gps&planIdentifier=123'
);
});
});
38 changes: 36 additions & 2 deletions packages/opensrp-plans/src/helpers/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
SERVICE_POINT_CHECK_CODE,
RECORD_GPS_CODE,
} from '@opensrp/plan-form-core';
import { HTTPMethod } from '@opensrp/server-service';
import { getFileNameFromCDHHeader, downloadFile } from '@opensrp/react-utils';
import { OpenSRPService } from './dataLoaders';

/**
* helper to retrieve the plan Type from a plan definition object
Expand Down Expand Up @@ -62,7 +65,38 @@ export const PlanLoading = () => {
return <Spin size="large" className="custom-spinner"></Spin>;
};

export const BuildDownloadUrl = (baseURL: string, planId: string) => {
const getFetchOptions = (_: AbortSignal, accessToken: string, method: HTTPMethod): RequestInit => {
return {
headers: {
authorization: `Bearer ${accessToken}`,
},
method,
};
};

/**
* download a misisons data.
*
* @param baseURL - opensrp server base url
* @param plan - plan whose mission data we fetching.
*/
export const downloadMissionData = async (baseURL: string, plan: PlanDefinition) => {
const { title, identifier } = plan;
const fileTitle = title.replaceAll(/\\s/g, ' ').split(' ').join('_');
const eventType = `${FLAG_PROBLEM_CODE},${SERVICE_POINT_CHECK_CODE},${LOOKS_GOOD_CODE},${RECORD_GPS_CODE}`;
return `${baseURL}${OPENSRP_TASK_EXPORT_DATA}?eventTypes=${eventType}&planIdentifier=${planId}`;
const exportPath = `${OPENSRP_TASK_EXPORT_DATA}?eventTypes=${eventType}&planIdentifier=${identifier}`;

const serve = new OpenSRPService(exportPath, baseURL, getFetchOptions);
const response = await serve.download();

// get filename from content-disposition header
const contentDispositionHeader = response.headers.get('content-disposition');
const fileName = contentDispositionHeader
? getFileNameFromCDHHeader(contentDispositionHeader)
: `${fileTitle}_${Date.now()}.zip`;

// get blob data from response
const blob = await response.blob();

downloadFile(blob, fileName);
};

0 comments on commit 3d37537

Please sign in to comment.