Skip to content

Commit

Permalink
Merge pull request #303 from bcgov/feat/edit-operator
Browse files Browse the repository at this point in the history
Feat: edit operator
  • Loading branch information
matthieu-foucault committed Mar 9, 2022
2 parents bf8874a + 8657bb0 commit d72ef66
Show file tree
Hide file tree
Showing 26 changed files with 1,018 additions and 49 deletions.
8 changes: 2 additions & 6 deletions app/components/Contact/ContactForm.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { Button } from "@button-inc/bcgov-theme";
import FormBase from "components/Form/FormBase";
import FormComponentProps from "components/Form/Interfaces/FormComponentProps";
import contactSchema from "data/jsonSchemaForm/contactSchema";
import { JSONSchema7 } from "json-schema";
import { FormPageFactoryComponentProps } from "lib/pages/relayFormPageFactory";

const uiSchema = {
comments: { "ui:widget": "TextAreaWidget" },
phone: { "ui:widget": "PhoneNumberWidget" },
};

interface Props extends FormComponentProps {
onDiscard: () => void;
}

const ContactForm: React.FC<Props> = (props) => {
const ContactForm: React.FC<FormPageFactoryComponentProps> = (props) => {
return (
<FormBase
{...props}
Expand Down
20 changes: 20 additions & 0 deletions app/components/Operator/OperatorForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Button } from "@button-inc/bcgov-theme";
import FormBase from "components/Form/FormBase";
import operatorSchema from "data/jsonSchemaForm/operatorSchema";
import { JSONSchema7 } from "json-schema";
import { FormPageFactoryComponentProps } from "lib/pages/relayFormPageFactory";

const OperatorForm: React.FC<FormPageFactoryComponentProps> = (props) => {
return (
<FormBase {...props} schema={operatorSchema as JSONSchema7} uiSchema={{}}>
<Button type="submit" style={{ marginRight: "1rem" }}>
Submit
</Button>
<Button type="button" variant="secondary" onClick={props.onDiscard}>
Discard Changes
</Button>
</FormBase>
);
};

export default OperatorForm;
36 changes: 36 additions & 0 deletions app/cypress/integration/cif/operators.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,40 @@ describe("the operators page", () => {
cy.get("thead th").contains("Operator Legal Name").click();
cy.contains("third operator legal name");
});

it("allows an operator to be edited", () => {
cy.visit("/cif/operators");
cy.get("button").contains("View").click();
cy.contains("Operator Information");
cy.get("body").happoScreenshot({
component: "Operator View Page",
});
cy.contains("first operator legal name");
cy.contains("first operator trade name");
cy.contains("ABCD");
cy.get("button").contains("Edit").click();
cy.contains("Edit Operator");
cy.get("body").happoScreenshot({
component: "Operator Edit Page",
});
cy.get("input[aria-label='Legal Name']").should(
"have.value",
"first operator legal name"
);
cy.get("input[aria-label='Trade Name']").should(
"have.value",
"first operator trade name"
);
cy.get("input[aria-label='BC Registry ID (optional)']").should(
"have.value",
"AB1234567"
);
cy.get("input[aria-label='Operator Code (optional)']").should(
"have.value",
"ABCD"
);
cy.get("input[aria-label='Trade Name']").clear().type("Updated");
cy.get("button").contains("Submit").click();
cy.get("table").contains("Updated");
});
});
30 changes: 30 additions & 0 deletions app/data/jsonSchemaForm/operatorSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const schema = {
$schema: "http://json-schema.org/draft-07/schema",
type: "object",
title: "Operator",
required: ["legalName", "tradeName"],
properties: {
legalName: {
type: "string",
title: "Legal Name",
default: undefined,
},
tradeName: {
type: "string",
title: "Trade Name",
default: undefined,
},
bcRegistryId: {
type: "string",
title: "BC Registry ID",
pattern: "^[a-zA-Z]{1,3}[0-9]{7}$",
},
operatorCode: {
type: "string",
title: "Operator Code",
pattern: "^[A-Z]{4}$",
},
},
};

export default schema;
2 changes: 2 additions & 0 deletions app/data/jsonSchemaForm/validationSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import projectContactSchema from "./projectContactSchema";
import projectManagerSchema from "./projectManagerSchema";
import projectSchema from "./projectSchema";
import contactSchema from "./contactSchema";
import operatorSchema from "./operatorSchema";

const validationSchemas = {
project_contact: projectContactSchema,
project_manager: projectManagerSchema,
project: projectSchema,
contact: contactSchema,
operator: operatorSchema,
};

export default validationSchemas;
170 changes: 170 additions & 0 deletions app/lib/pages/relayFormPageFactory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { RelayProps } from "relay-nextjs";
import { graphql, usePreloadedQuery } from "react-relay/hooks";
import { useUpdateFormChange } from "mutations/FormChange/updateFormChange";
import { useDeleteFormChange } from "mutations/FormChange/deleteFormChange";
import { useEffect, useMemo } from "react";
import { useRouter } from "next/router";
import DefaultLayout from "components/Layout/DefaultLayout";
import SavingIndicator from "components/Form/SavingIndicator";
import FormComponentProps from "components/Form/Interfaces/FormComponentProps";
import { relayFormPageFactoryQuery } from "__generated__/relayFormPageFactoryQuery.graphql";
import { GraphQLTaggedNode } from "relay-runtime";

export interface FormPageFactoryComponentProps extends FormComponentProps {
onDiscard: () => void;
}

const pageQuery = graphql`
query relayFormPageFactoryQuery($form: ID!) {
session {
...DefaultLayout_session
}
formChange(id: $form) {
id
newFormData
formDataRecordId
updatedAt
}
}
`;

/**
* A factory function that creates a page component for a form_change record.
*
* It abstracts away the submit, discard and change behaviours from the
* individual forms, as well as the relay form change query, session and layouts.
*
* @param resourceTitle The title of the resource, for which the form is being created (e.g. "Operator")
* @param onSubmitOrDiscardRoute The route to navigate to when the form is submitted or discarded
* @param FormComponent The form component to render
* @returns a tuple [FormPage, query] where FormPage is the page component to render, and query is the relay query.
* it is meant to be used in the withRelay HOC (e.g. withRelay(FormPage, query, withRelayOptions)).
*/

const relayFormPageFactory = (
resourceTitle: string,
onSubmitOrDiscardRoute: any,
FormComponent: React.FC<FormPageFactoryComponentProps>
) => {
function FormPage({
preloadedQuery,
}: RelayProps<{}, relayFormPageFactoryQuery>) {
const { session, formChange } = usePreloadedQuery(
pageQuery,
preloadedQuery
);

const [updateFormChange, isUpdatingFormChange] = useUpdateFormChange();
const [deleteFormChange, isDeletingFormChange] = useDeleteFormChange();
const router = useRouter();

const lastEditedDate = useMemo(
() => new Date(formChange?.updatedAt),
[formChange?.updatedAt]
);

useEffect(() => {
if (!formChange) router.replace("/404");
}, [formChange, router]);
if (!formChange) {
return null;
}

const isEditing = formChange.formDataRecordId !== null;

const handleChange = ({ formData }) => {
updateFormChange({
variables: {
input: {
id: formChange.id,
formChangePatch: {
newFormData: formData,
},
},
},
optimisticResponse: {
updateFormChange: {
formChange: {
id: formChange.id,
newFormData: formData,
},
},
},
debounceKey: formChange.id,
onError: (e) => console.log(e),
});
};

const handleSubmit = ({ formData }) => {
updateFormChange({
variables: {
input: {
id: formChange.id,
formChangePatch: {
newFormData: formData,
changeStatus: "committed",
},
},
},
debounceKey: formChange.id,
onCompleted: () => {
router.push(onSubmitOrDiscardRoute);
},
onError: (e) => console.log(e),
});
};

const handleDiscard = () => {
deleteFormChange({
variables: {
input: {
id: formChange.id,
},
},
onCompleted: () => {
router.push(onSubmitOrDiscardRoute);
},
});
};

return (
<DefaultLayout session={session}>
<header>
<h2>
{isEditing ? "Edit" : "New"} {resourceTitle}
</h2>
<SavingIndicator
isSaved={!isUpdatingFormChange}
lastEdited={lastEditedDate}
/>
</header>
<FormComponent
formData={formChange.newFormData}
disabled={isDeletingFormChange || isUpdatingFormChange}
onChange={handleChange}
onSubmit={handleSubmit}
onDiscard={handleDiscard}
/>
<style jsx>{`
header {
display: flex;
justify-content: space-between;
align-items: start;
}
header h2 {
padding-right: 10px;
}
`}</style>
</DefaultLayout>
);
}

return [FormPage, pageQuery] as [
({
preloadedQuery,
}: RelayProps<{}, relayFormPageFactoryQuery>) => JSX.Element,
GraphQLTaggedNode
];
};

export default relayFormPageFactory;
36 changes: 36 additions & 0 deletions app/mutations/Operator/createEditOperatorFormChange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useMutation, graphql, Disposable, Environment } from "react-relay";
import { MutationConfig } from "relay-runtime";
import { createEditOperatorFormChangeMutation } from "__generated__/createEditOperatorFormChangeMutation.graphql";

export const mutation = graphql`
mutation createEditOperatorFormChangeMutation($operatorRowId: Int!) {
createFormChange(
input: {
formDataSchemaName: "cif"
formDataTableName: "operator"
jsonSchemaName: "operator"
operation: UPDATE
changeReason: "Created from operator form"
formDataRecordId: $operatorRowId
}
) {
formChange {
id
}
}
}
`;

export const useCreateEditOperatorFormChange = (
commitMutationFn?: (
environment: Environment,
config: MutationConfig<createEditOperatorFormChangeMutation>
) => Disposable
) => {
return useMutation<createEditOperatorFormChangeMutation>(
mutation,
commitMutationFn
);
};

export default useCreateEditOperatorFormChange;
33 changes: 33 additions & 0 deletions app/mutations/Operator/createNewOperatorFormChange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useMutation, graphql, Disposable, Environment } from "react-relay";
import { MutationConfig } from "relay-runtime";
import { createNewOperatorFormChangeMutation } from "__generated__/createNewOperatorFormChangeMutation.graphql";

export const mutation = graphql`
mutation createNewOperatorFormChangeMutation {
createFormChange(
input: {
formDataSchemaName: "cif"
formDataTableName: "operator"
jsonSchemaName: "operator"
operation: CREATE
changeReason: "Created from operator form"
}
) {
formChange {
id
}
}
}
`;

export const useCreateNewOperatorFormChange = (
commitMutationFn?: (
environment: Environment,
config: MutationConfig<createNewOperatorFormChangeMutation>
) => Disposable
) => {
return useMutation<createNewOperatorFormChangeMutation>(
mutation,
commitMutationFn
);
};
Loading

0 comments on commit d72ef66

Please sign in to comment.