Skip to content

Commit

Permalink
[MDS-5712] AMS Application Purpose (#3036)
Browse files Browse the repository at this point in the history
* change around authorizations involved, convert to TS, fix some issues in Callout, GroupCheckbox

* add 'help' text to radio buttons, make authorizations form section mostly match mockup

* make field names closer to mappings, tooltip

* migration to separate out AMS amended authorizations into their own rows, add new columns

* update BE response to include new fields, format a bit differently when received on the FE, and update the form accordingly. Fix some validation, radio button, checkbox label that is a form section issues

* fix issue with label and with radio buttons, process & validate on BE, transform payload on FE

* move authorizations involved into common, put in CORE, change 'OTHER' and 'MINES ACT PERMIT' UI according to AC/mockups, make the list of checkboxes be validated

* make a hidden field component to handle most of the redux-y stuff, update authorization interface, update data mocks

* fix style issues on CORE

* fix issues with multiselect changing value onBlur, fix issues with saving on CORE, and bug on new records

* add loading indicator on multiselect, remove console log, snaps

* remove failing line from cypress test

* fill in an authorization for cypress test

* make required fields consistent and also don't show an empty item in multiselect

* rename migration for order, and update data with 'OTHER' type to use authorization description
  • Loading branch information
taraepp authored Apr 5, 2024
1 parent 59913a1 commit e79a288
Show file tree
Hide file tree
Showing 40 changed files with 4,074 additions and 835 deletions.
87 changes: 87 additions & 0 deletions migrations/sql/V2024.03.23.1.46__ams_authorizations.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
ALTER TABLE project_summary_authorization
ADD COLUMN IF NOT EXISTS amendment_changes text[], -- array of change types
ADD COLUMN IF NOT EXISTS amendment_severity varchar(3), -- SIG or MIN
ADD COLUMN IF NOT EXISTS is_contaminated boolean,
ADD COLUMN IF NOT EXISTS new_type varchar(3), -- permit | approval
ADD COLUMN IF NOT EXISTS authorization_description varchar(4000),
ADD COLUMN IF NOT EXISTS exemption_requested boolean,
ALTER COLUMN existing_permits_authorizations SET DEFAULT array[]::text[],
ALTER COLUMN existing_permits_authorizations DROP NOT NULL
;

UPDATE project_summary_authorization
SET authorization_description = array_to_string(existing_permits_authorizations, ',')
WHERE project_summary_authorization_type = 'OTHER';

DO
$$
DECLARE
authorization record;
auth_no text;
auth_array text[];
i integer;
n integer;

other_types text[];
type_count integer;
delete_guids uuid[];
BEGIN
-- target authorizations with multiple auth # amendments related to EMA
FOR authorization IN SELECT * FROM project_summary_authorization
WHERE 'AMENDMENT'=ANY(project_summary_permit_type)
AND cardinality(existing_permits_authorizations) > 1
AND project_summary_authorization_type IN(
SELECT project_summary_authorization_type FROM project_summary_authorization_type
WHERE project_summary_authorization_type_group_id = 'ENVIRONMENTAL_MANAGMENT_ACT')
LOOP
-- create a record for each auth no
i := 1;
n := cardinality("authorization"."existing_permits_authorizations");
auth_array := "authorization"."existing_permits_authorizations";

type_count := cardinality("authorization"."project_summary_permit_type");
IF type_count = 1 THEN
delete_guids := delete_guids || "authorization"."project_summary_authorization_guid";
ELSE
other_types := array_remove("authorization"."project_summary_permit_type", 'AMENDMENT');
UPDATE project_summary_authorization
SET project_summary_permit_type = other_types, existing_permits_authorizations = ARRAY[]::text[]
WHERE project_summary_authorization_guid = "authorization"."project_summary_authorization_guid";
END IF;

LOOP
auth_no := TRIM(auth_array[i]);
IF auth_no != '' THEN
INSERT INTO project_summary_authorization (
project_summary_guid,
project_summary_authorization_type,
project_summary_permit_type,
existing_permits_authorizations,
deleted_ind,
create_user,
create_timestamp,
update_user,
update_timestamp
) VALUES (
"authorization"."project_summary_guid",
"authorization"."project_summary_authorization_type",
'{"AMENDMENT"}',
ARRAY [auth_no],
"authorization"."deleted_ind",
"authorization"."create_user",
"authorization"."create_timestamp",
"authorization"."update_user",
"authorization"."update_timestamp"
);
END IF;

i := i + 1;
EXIT WHEN i > n;

END LOOP;
END LOOP;

DELETE FROM project_summary_authorization
WHERE project_summary_authorization.project_summary_authorization_guid =ANY(delete_guids);
END
$$
2 changes: 1 addition & 1 deletion services/common/src/components/common/Callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CALLOUT_SEVERITY } from "@mds/common/constants/strings";
interface CallOutProps {
message: string | ReactNode;
title?: string;
severity: string;
severity?: string;
}

const Callout: FC<CallOutProps> = ({ message, title, severity = CALLOUT_SEVERITY.info }) => {
Expand Down
8 changes: 4 additions & 4 deletions services/common/src/components/forms/BaseInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,26 +100,26 @@ export const getFormItemLabel = (
}
if (isRequired) {
return (
<>
<div style={{ width: "100%" }}>
{label}
{labelSubtitle && (
<>
<br />
<span className="label-subtitle">{labelSubtitle}</span>
</>
)}
</>
</div>
);
}
return (
<>
<div>
{label} <span className="form-item-optional">&nbsp;(optional)</span>
{labelSubtitle && (
<>
<br />
<span className="label-subtitle">{labelSubtitle}</span>
</>
)}
</>
</div>
);
};
77 changes: 41 additions & 36 deletions services/common/src/components/forms/RenderGroupCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
import React from "react";
import PropTypes from "prop-types";
import { change } from "redux-form";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import React, { FC } from "react";
import { Checkbox, Form } from "antd";
import { BaseInputProps } from "./BaseInput";

/**
* @constant RenderGroupCheckbox - Ant Design `Checkbox` component for redux-form.
* NOTE ABOUT A REDUX BUG AFFECTING THIS COMPONENT:
* Exactly what's happening here: https://github.com/redux-form/redux-form/issues/2768
* - When onBlur is called, it calls onChange to update the value
* - but with a group checkbox, it does it with the value of the individual checkbox,
* - not with the value of the group
* - so it will call onChange(true | false) instead of onChange(["val1", "val2"])
* - which causes an error.
* - And event.preventDefault() on the onBlur also prevents validation
* - The best and easiest solution I found was to put a normalize function on the <Field />
* - to not update the value if it's not an array (normalize gets called first)
* - It is exported here as normalizeGroupCheckBox
*/

const propTypes = {
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
label: PropTypes.string.isRequired,
meta: PropTypes.objectOf(PropTypes.any).isRequired,
input: PropTypes.objectOf(PropTypes.string).isRequired,
disabled: PropTypes.bool.isRequired,
options: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)).isRequired,
change: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
setInitialValues: PropTypes.func,
};
interface CheckboxProps extends BaseInputProps {
label: string;
options: any;
defaultValue: any[];
}

const onChange = (checkedValues, change, form, name) => {
change(form, name, checkedValues);
};
export const normalizeGroupCheckBox = (val, prev) => (Array.isArray(val) ? val : prev);

const RenderGroupCheckbox = (props) => {
const RenderGroupCheckbox: FC<CheckboxProps> = ({
meta,
input,
label,
options,
required,
...props
}) => {
return (
<Form.Item
label={props.label}
validateStatus={props.meta.touched ? props.meta.error && "error" : ""}
name={input.name}
label={label}
required={required}
validateStatus={meta.touched ? meta.error && "error" : ""}
help={
meta.touched &&
((meta.error && <span>{meta.error}</span>) || (meta.warning && <span>{meta.warning}</span>))
}
getValueProps={() => ({ value: input.value })}
>
<Checkbox.Group
// apparently id & checked do not exist on Checkbox.Group
// id={props.id}
name={props.name}
// checked={props.input.value}
options={props.options}
name={input.name}
options={options}
disabled={props.disabled}
defaultValue={props.formValues[props.fieldName]}
value={
props.setInitialValues && !props.meta.dirty ? props.setInitialValues() : props.input.value
}
onChange={(values) => onChange(values, props.change, props.formName, props.fieldName)}
defaultValue={props.defaultValue}
{...input}
/>
</Form.Item>
);
};

RenderGroupCheckbox.propTypes = propTypes;

const mapDispatchToProps = (dispatch) => bindActionCreators({ change }, dispatch);
export default connect(null, mapDispatchToProps)(RenderGroupCheckbox);
export default RenderGroupCheckbox;
82 changes: 82 additions & 0 deletions services/common/src/components/forms/RenderHiddenField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { FC, useState, useEffect } from "react";
import { Input, Form } from "antd";
import { BaseInputProps, getFormItemLabel } from "./BaseInput";

/**
* Just can't conform the UI to play nice with redux-form?
* This will display the label and handle validation on change and submit
* Can handle string, number, string[] values, can probably use normalize on Field to coerce
USAGE
- the main thing to note is that you should be handling the redux change to update the value
- making the "actual" input the children will put help/validation messages at the bottom
- if this is not desired, put underneath <Field /> instead,
- but there is a min-height on form-item that will have to be dealt with...
<Field
name="field_name"
component={RenderHiddenField}
required
validate={[required]}
label="Label"
>
<Checkbox
value
checked
onChange={(e) => dispatch(change(FORM_NAME, "field_name", ["5"]))}
>
Checkbox label
</Checkbox>
{whatever else is necessary to display}
</Field>
*/
const RenderHiddenField: FC<BaseInputProps> = ({
label,
labelSubtitle,
help,
meta,
input,
disabled,
required,
defaultValue,
children,
}) => {
const [touched, setTouched] = useState(meta.touched);

useEffect(() => {
// dirty catches when the value is set & cleared,
// touched should catch when it's submitted
if (meta.dirty || meta.touched) {
setTouched(true);
}
}, [meta.dirty, meta.touched]);

return (
<Form.Item
className="form-item-hidden"
name={input.name}
required={required}
label={getFormItemLabel(label, required, labelSubtitle)}
validateStatus={touched ? (meta.error && "error") || (meta.warning && "warning") : ""}
help={
touched &&
((meta.error && <span>{meta.error}</span>) || (meta.warning && <span>{meta.warning}</span>))
}
>
<>
<div style={{ display: "none" }}>
<Input
disabled={disabled}
defaultValue={defaultValue}
name={input.name}
value={input.value}
/>
</div>
{children}
{help && <div className={`form-item-help ${input.name}-form-help`}>{help}</div>}
</>
</Form.Item>
);
};

export default RenderHiddenField;
6 changes: 3 additions & 3 deletions services/common/src/components/forms/RenderMultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface MultiSelectProps extends BaseInputProps {
data: IOption[];
filterOption?: any;
onSearch?: any;
loading?: boolean;
}

export const RenderMultiSelect: FC<MultiSelectProps> = (props) => {
Expand Down Expand Up @@ -50,6 +51,7 @@ export const RenderMultiSelect: FC<MultiSelectProps> = (props) => {
((meta.error && <span>{meta.error}</span>) ||
(meta.warning && <span>{meta.warning}</span>))
}
getValueProps={() => input.value !== "" && { value: input.value }}
>
<Select
loading={props.loading}
Expand All @@ -59,11 +61,9 @@ export const RenderMultiSelect: FC<MultiSelectProps> = (props) => {
mode="multiple"
size="small"
placeholder={placeholder}
{...input}
id={props.id}
id={props.id ?? props.input.name}
onSearch={onSearch}
options={data}
value={input.value ?? undefined}
onChange={input.onChange}
filterOption={filterOption || caseInsensitiveLabelFilter}
showArrow
Expand Down
23 changes: 14 additions & 9 deletions services/common/src/components/forms/RenderRadioButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const RenderRadioButtons: FC<RenderRadioButtonsProps> = ({
disabled = false,
input,
id,
help,
customOptions,
required = false,
optionType = "default",
Expand Down Expand Up @@ -47,15 +48,19 @@ const RenderRadioButtons: FC<RenderRadioButtonsProps> = ({
}
label={getFormItemLabel(label, required)}
>
<Radio.Group
disabled={disabled}
name={input.name}
onChange={handleRadioChange}
options={options}
optionType={optionType}
buttonStyle="solid"
{...(isVertical && { className: "vertical-radio-group" })}
/>
<>
<Radio.Group
disabled={disabled}
name={input.name}
value={input.value}
onChange={handleRadioChange}
options={options}
optionType={optionType}
buttonStyle="solid"
{...(isVertical && { className: "vertical-radio-group" })}
/>
{help && <div className={`form-item-help ${input.name}-form-help`}>{help}</div>}
</>
</Form.Item>
);
};
Expand Down
Loading

0 comments on commit e79a288

Please sign in to comment.