diff --git a/app/frontend/components/bill/creator/BillCreator.tsx b/app/frontend/components/bill/creator/BillCreator.tsx index 794c6e9c..1475c4b4 100644 --- a/app/frontend/components/bill/creator/BillCreator.tsx +++ b/app/frontend/components/bill/creator/BillCreator.tsx @@ -1,22 +1,17 @@ import { ROUTES, Support } from "app/frontend/sway_constants"; -import { getId, getSelectValue, isCongressLocale, logDev, handleError, notify } from "app/frontend/sway_utils"; -import { Formik, Form as FormikForm } from "formik"; +import { getId, getSelectValue, handleError, isCongressLocale, logDev, notify } from "app/frontend/sway_utils"; +import { Formik } from "formik"; import { useCallback, useRef } from "react"; -import { Button } from "react-bootstrap"; -import { FiSave } from "react-icons/fi"; import { sway } from "sway"; import * as yup from "yup"; import { router, usePage } from "@inertiajs/react"; -import BillCreatorFields from "app/frontend/components/admin/creator/BillCreatorFields"; import { ISubmitValues } from "app/frontend/components/admin/types"; -import SwaySpinner from "app/frontend/components/SwaySpinner"; import { useAxiosPost } from "app/frontend/hooks/useAxios"; -import BillComponent from "app/frontend/components/bill/BillComponent"; - import { useNewBillInitialValues } from "app/frontend/components/bill/creator/hooks/useNewBillInitialValues"; import { useLocale } from "app/frontend/hooks/useLocales"; +import BillCreatorFormikForm from "app/frontend/components/bill/creator/BillCreatorFormikForm"; const VALIDATION_SCHEMA = yup.object().shape({ externalId: yup.string().required(), @@ -36,7 +31,11 @@ const VALIDATION_SCHEMA = yup.object().shape({ senateRollCallVoteNumber: yup.number().nullable().notRequired(), }); -const BillCreator = () => { +interface IProps { + setCreatorDirty: React.Dispatch>; +} + +const BillCreator: React.FC = ({ setCreatorDirty }) => { const summaryRef = useRef(""); const [locale] = useLocale(); const bill = usePage().props.bill as sway.IBill; @@ -230,109 +229,12 @@ const BillCreator = () => { validationSchema={VALIDATION_SCHEMA} onSubmit={handleSubmit} enableReinitialize={true} - onReset={() => logDev("RESET FORMIK")} - > - {(formik) => { - return ( - <> - - -
-
-
- -
-
-
-
- -
-
-
-
-
-
Bill of the Week Preview
- - ({ - support: Support.For, - summary: p.summary, - organization: { - id: p.value, - name: p.label, - iconPath: p.iconPath, - }, - }) as sway.IOrganizationPosition, - ) - .concat( - formik.values.organizationsOppose.map( - (p) => - ({ - support: Support.Against, - summary: p.summary, - organization: { - id: p.value, - name: p.label, - iconPath: p.iconPath, - }, - }) as sway.IOrganizationPosition, - ), - )} - sponsor={ - legislators.find( - (l) => l.id === (formik.values.legislator?.value as number), - ) as sway.ILegislator - } - legislatorVotes={formik.values.supporters - .map( - (s) => - ({ - legislatorId: s.value, - support: Support.For, - billId: bill.id || -1, - }) as sway.ILegislatorVote, - ) - .concat( - formik.values.opposers.map( - (s) => - ({ - legislatorId: s.value, - support: Support.Against, - billId: bill.id || -1, - }) as sway.ILegislatorVote, - ), - ) - .concat( - formik.values.abstainers.map( - (s) => - ({ - legislatorId: s.value, - support: Support.Abstain, - billId: bill.id || -1, - }) as sway.ILegislatorVote, - ), - ) - .flat()} - /> - - ); + onReset={() => { + logDev("RESET FORMIK"); + setCreatorDirty(false); }} + > + ); }; diff --git a/app/frontend/components/bill/creator/BillCreatorFormikForm.tsx b/app/frontend/components/bill/creator/BillCreatorFormikForm.tsx new file mode 100644 index 00000000..728c2c20 --- /dev/null +++ b/app/frontend/components/bill/creator/BillCreatorFormikForm.tsx @@ -0,0 +1,130 @@ +import { Support } from "app/frontend/sway_constants"; +import { Form as FormikForm, useFormikContext } from "formik"; +import { Button } from "react-bootstrap"; +import { FiSave } from "react-icons/fi"; +import { sway } from "sway"; + +import BillCreatorFields from "app/frontend/components/admin/creator/BillCreatorFields"; +import SwaySpinner from "app/frontend/components/SwaySpinner"; + +import BillComponent from "app/frontend/components/bill/BillComponent"; + +import { usePage } from "@inertiajs/react"; +import { ISubmitValues } from "app/frontend/components/admin/types"; +import { forwardRef, useEffect } from "react"; + +interface IProps { + setCreatorDirty: React.Dispatch>; +} + +const BillCreatorFormikForm = forwardRef(({ setCreatorDirty }: IProps, summaryRef: React.Ref) => { + const formik = useFormikContext(); + + const bill = usePage().props.bill as sway.IBill; + const legislators = usePage().props.legislators as sway.ILegislator[]; + + useEffect(() => { + setCreatorDirty(formik.dirty); + }, [setCreatorDirty, formik.dirty]); + + return ( + <> + + +
+
+
+ +
+
+
+
+ +
+
+
+
+
+
Bill of the Week Preview
+ void) | RefObject'. + summary: summaryRef?.current || "", + legislatorId: formik.values.legislator?.value as number, + }} + positions={formik.values.organizationsSupport + .map( + (p) => + ({ + support: Support.For, + summary: p.summary, + organization: { + id: p.value, + name: p.label, + iconPath: p.iconPath, + }, + }) as sway.IOrganizationPosition, + ) + .concat( + formik.values.organizationsOppose.map( + (p) => + ({ + support: Support.Against, + summary: p.summary, + organization: { + id: p.value, + name: p.label, + iconPath: p.iconPath, + }, + }) as sway.IOrganizationPosition, + ), + )} + sponsor={ + legislators.find((l) => l.id === (formik.values.legislator?.value as number)) as sway.ILegislator + } + legislatorVotes={formik.values.supporters + .map( + (s) => + ({ + legislatorId: s.value, + support: Support.For, + billId: bill.id || -1, + }) as sway.ILegislatorVote, + ) + .concat( + formik.values.opposers.map( + (s) => + ({ + legislatorId: s.value, + support: Support.Against, + billId: bill.id || -1, + }) as sway.ILegislatorVote, + ), + ) + .concat( + formik.values.abstainers.map( + (s) => + ({ + legislatorId: s.value, + support: Support.Abstain, + billId: bill.id || -1, + }) as sway.ILegislatorVote, + ), + ) + .flat()} + /> + + ); +}); + +export default BillCreatorFormikForm; diff --git a/app/frontend/components/bill/creator/BillOfTheWeekCreator.tsx b/app/frontend/components/bill/creator/BillOfTheWeekCreator.tsx index 82cf87dd..e415f1dc 100644 --- a/app/frontend/components/bill/creator/BillOfTheWeekCreator.tsx +++ b/app/frontend/components/bill/creator/BillOfTheWeekCreator.tsx @@ -3,7 +3,7 @@ /** @format */ import { ROUTES } from "app/frontend/sway_constants"; import { logDev, REACT_SELECT_STYLES } from "app/frontend/sway_utils"; -import { useCallback, useMemo } from "react"; +import { useCallback, useMemo, useState } from "react"; import { Button, ButtonGroup, Form, Nav, Tab } from "react-bootstrap"; import Select, { SingleValue } from "react-select"; import { ISelectOption, sway } from "sway"; @@ -39,6 +39,7 @@ const BillOfTheWeekCreator_: React.FC = ({ bills, bill, user, tabKey = E const { isAdmin } = user; const isLoading = useMemo(() => false, []); + const [isCreatorDirty, setCreatorDirty] = useState(false); const selectedBill = useMemo( () => @@ -69,11 +70,23 @@ const BillOfTheWeekCreator_: React.FC = ({ bills, bill, user, tabKey = E } }, []); - const handleChangeTab = useCallback((newTabKey: string | null) => { - if (!newTabKey) return; - - params.add("tabKey", newTabKey); - }, []); + const handleChangeTab = useCallback( + (newTabKey: string | null) => { + if (!newTabKey) return; + + if (isCreatorDirty && newTabKey === ETab.Schedule) { + const isConfirmed = window.confirm( + "Switching to the scheduler will remove all unsaved data from the Bill Creator. Continue?", + ); + if (isConfirmed) { + params.add("tabKey", newTabKey); + } + } else { + params.add("tabKey", newTabKey); + } + }, + [isCreatorDirty], + ); if (!isAdmin || !locale) { logDev("BillOfTheWeekCreator - no admin OR no locale - render null"); @@ -141,7 +154,7 @@ const BillOfTheWeekCreator_: React.FC = ({ bills, bill, user, tabKey = E - +